javascript继承机制

一 原型

    1. 基本概念

         JavaScript是一种基于原型继承的语言,不包含传统的类继承模型,没有“类”和“实例”的概念,全靠原型链实现继承。

      2. new 运算符

         传统类继承,通过new命令创建实例,Brendan Eich也把new命令引入了JavaScript,通过new 构造函数来创建对象

function Dog(name) {
   this.name = name;
   this.eat = function () { ...}
}
var daMao = new Dog("大毛");
var xiaoMao = new Dog("小毛");

       上面创建了两个对象,但是有个问题就是,daMao和xiaoMao各有个eat function的副本,这有点浪费资源,怎么办呢。

    3. prototype属性的引入

         Brendan Eich为构造函数设置了一个prototype属性,这个属性指向一个对象(原型对象),所有实例对象共享该对象的方法和属性。给出如下代码       

    function Dog(name) {
        this.name = name;
     }       
    Dog.prototype = {
        eat: function() {
        //eat something
        }
    }

     这就是原型继承的实现方式了,然后通过
           var dogA = new Dog("大狗");
     就可以创建新的dogA对象。

     new运算符做了哪些工作呢,

         (1) 创建一个空的对象,对象的__proto__属性指向Dog.prototype;

         (2) 初始化对象,函数Dog被传入参数并调用,this指向该对象。

         (3) 返回对象。

     4. 原型链

         大多数JavaScript实现用一个__proto__属性来表示一个对象的原型链。当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。—JavaScript秘密花园

       Vjeux在Javascript – How Prototypal Inheritance really works中给出一段代码用于解释JS引擎如何查找属性:

function getProperty(obj, prop) {
     if (obj.hasOwnProperty(prop))
         return obj[prop]
     else if (obj.__proto__ !== null)
         return getProperty(obj.__proto__, prop)
     else
         return undefined;
}
        上述dogA的原型链如下:

        dogA
               Dog.prototype
               {eat: function() {}}
                      Object.prototype
                       {toString: ... , hasOwnProperty ...}

         dogA具有原型链上对象的所有方法。

      总结:

        所有的实例对象共享同一个prototype对象,实例对象就好像"继承"了prototype对象一样。下面给出一张图(出自 ECMAScript规范)来解释原型继承的工作原理。

       javascript继承机制_第1张图片

二 hasOwnProperty 和 for in

      通过上述知道查找对象属性时会向上遍历原型链,如果一个属性在原型链的上端,则对于查找时间将带来不利影响。特别的,试图获取一个不存在的属性将会遍历整个原型链。
      有时只需要判断对象是否具有自定义属性而不是原型链上的属性,这时候就必须使用“继承”自 Object.prototype 的 hasOwnProperty 方法。
Object.prototype.bar = 1; 
var foo = {goo: undefined};
foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true
注意: hasOwnProperty是可以被覆盖的,
var foo = {
  hasOwnProperty: function() {
    return false;
  },
  bar: 'Here be dragons'
};
foo.hasOwnProperty('bar'); // always returns false
      当hasOwnProperty被覆盖时,可以通过如下方式来调用外部的hasOwnProperty函数
({}).hasOwnProperty.call(foo, 'bar'); // true
Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
      当使用 for in 循环遍历对象的属性时,原型链上的所有属性都将被访问。可以通过Object.prototype原型上的hasOwnProperty函数来过滤不希望出现的属性。

for (var i in foo) {
    if (foo.hasOwnProperty(i)) {
        console.log(i);
    }
}

      不仅是过滤不希望出现的属性,当JS对象被扩展时,使用for in遍历属性时可能会出错。Prototype类库就扩展了内置对象,因此当页面包含这个类库时,不使用hasOwnProperty过滤的for in循环可能会出问题。

Reference

       [1] Javascript继承机制的设计思想.

       [2] JavaScript原型继承工作原理

       [3] Prototypal Inheritance in JavaScript

 

你可能感兴趣的:(javascript继承机制)