JavaScript: 对象的定义、实例及内部机制

@ 2011-01-06 00:45:48
标签:

    Object是编程中的重要话题。严格来说,讨论Object时,需要区分Object和Object Instance两个概念。

    Object 的定义、实例和访问

    function Person(){
      this.name = 'Sean';
      this.age    = 30;
    }
    

    上面的例子定义了一个叫做'Person'的Object,它有两个属性'name'和'age'。没错,它看起来像是一个function(函数),但这的确是最基本的定义一个对象的方法。

    我们定义一个对象是为了使用它,这需要一个对象实例(Object Instance)。可以通过new操作符来得到一个Object Instance,比如:

    var person_a = new Person();
    person_a.name = 'Name 1';
    
    var person_b = new Person();
    
    alert(person_a.name);
    alert(person_b.name);
    alert(person_a['name']);
    

    上面的例子不仅展示了如何得到一个对象实例(Object Instance),同时也展示了如何访问对象的属性。访问对象的属性可以通过 .[]操作符来实现,两者是等价的。通过[]的方式,使得我们可以动态决定属性名,或者使用不符合JavaScript变量命名规定的属性名。比如下面的例子:

    person_a['office address'] = 'some address';
    var name = 'home address';
    person_a[name] = 'some address 2';
    
    alert(person_b[name]);  // undefined
    

    上面的例子也表明,一个Object Instance除了拥有它原有定义的属性外,是可以添加新的属性的。这些新添加的属性只存在于这个Object Instance本身,不影响其它的Object Instance。

    不通过定义直接得到Object Instance

    JavaScript允许在没有Object的定义的情况下,直接得到一个Object Instance。

    var person_c = {name:'Zhang San', age:23};
    alert(person_c.name);
    

    从效果上来说,上面例子中的person_c和之前例子中的person_a或者person_b并没有太大区别,写法上却更简单。

    如果要访问的属性不存在

    比如上面Person的对象实例person_a,我们访问它的属性'phone'的话,因为该属性不存在,所以会返回 undefined。实际上访问对象属性的过程要比这个复杂。其过程可以描述如下:

    function getObjectAttribute(obj, name){
      var p = obj;
      while(p!=null){
       if(p.hasOwnProperty('name')) return p.name;
       p = obj.__proto__;
      }
      return null;
    }
    

    上面程序片段中,hasOwnProperty表示对象本身是否定义了这个属性,如果没有定义,则试图沿着一个特殊的属性proto逐级向上查找要访问的属性名。

    聪明的你也许会想到,这似乎有点对象继承的意思了。没错,这正是JavaScript中实现对象继承的关键。

    __proto__ 属性的值来自何处

    上面提到,在得到一个对象实例的时候,实际上有两种方法:new或者{}的方式。对于 {}的方式,它的proto属性无所从来(实际上是一个Object对象的实例)。对于new的方式,proto属性会被赋值为对象定义的 prototype 属性。用代码可以表示如下

    var person_a = new Peron();
    person_a.__proto__ = Person.prototype;   // 会由JavaScript Engine 自动执行
    

    至此,一种实现对象继承的方法也就出来了。

    对象定义的prototype属性和{}得到的对象实例的proto属性的默认值是Object对象的一个实例。据此,我们也许可以给所有的对象都添加一个”全局"方法。下面的代码在Firefox中运行通过,所有的对象实例都有了一个println()方法。

    Object.prototype.println = function() {
      alert('I am here');
    };
    
    function Person(){
      this.name = 'abc';
    }
    
    var p = new Person();
    p.println();
    
    var o = {name:'Sean', age:30};
    o.println();
    

    对象的属性和方法

    由于JavaScript中,函数(function)其实是Function Object的实例,因而对象的方法和属性就其本质而言没有区别。

    function Person(fname, sname){
      this.firstName = fname,
      this.secondName = sname;
      this.fullName = function(){
        return this.fname + ', ' + this.secondName;
      };
    }
    
    var person = new Person('Sean', 'Zhang');
    alert(this.fullName());
    

    上面的例子定义了对象的方法'fullName',同时也展示了如何初始化对象——利用函数的参数。

    this 指针

    上面很多代码中都出现了this这个关键字,它表示访问该属性或方法的对象实例。看下面的例子:

    function outside_fullName(){
        return this.fname + ', ' + this.secondName;
    };
    
    function Person(fname, sname){
      this.firstName = fname,
      this.secondName = sname;
      this.fullName = outside_fullName;
    }
    
    var person = new Person('Sean', 'Zhang');
    alert(this.fullName());
    

    这个例子和之前的例子实际效果相同,但却把方法的定义放在了对象定义的外部。虽然在定义方法时还不知道对象是谁,但却通过this这个特殊关键字来表示发起调用的对象实例。

    JavaScript对象的继承

    利用prototype的对象继承

    function Person (name) {
      this.name = name;
    }
    
    function Employee(title){
      this.title = title;
    }
    
    Employee.prototype = new Person();
    

    利用call或apply的对象继承

    function Employee(name, title){
      Person.call(this, name);
      this.title = title;
    }
    

    利用这种方法,可以实现多继承。

    参考资源

    标签:

      分享到:
      comments powered by Disqus

      31/35ms