JS 原型以及原型链

 

 

    /**
     * 原型对象
     * 无论什么时,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性
     * 这个属性指向该函数的原型对象。默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)
     * 属性,这个属性包含一个指向 prototype 属性所在函数的指针。当构造函数创建一个新实例后,
     * 该实例的内部包含一个指针[[Prototype]](内部属性),指向构造函数的原型对象。
     */

    /**
     * 如:
     * 创建一个函数 A:function A(){}
     * function A(){} 会有一个属性: prototype 属性
     * function A(){} 的原型对象为: A.prototype
     * function A(){} 的原型对象会自动获得一个 constructor 属性: A.prototype.constructor
     * 那么:
     * function A(){} 中的 prototype 属性 ---(指向)---> A.prototype (function A(){} 的原型对象)
     * A.prototype.constructor (constructor属性) ---(指向)---> function A(){} (函数 A)
     */
    function A(){}
    console.log(A.prototype.constructor==A);//true 可见原型对象中的 constructor 指向函数A

    /**
     * 如:
     * 使用 new 关键字结合 A() 函数来创建实例 obj
     * 则 obj 这个实例会有一个 [[Prototype]] 内部属性指向 A 的原型对象 A.prototype
     * Firefox、Safari 和Chrome 在每个对象上都支持一个属性 __proto__ ,它就相当于 [[Prototype]]
     */

    var obj=new A();
    console.log(obj.__proto__==A.prototype);//true 可见实例中的 [[Prototype]] 指向函数原型 A.prototype
    
    A.prototype.name="guang";//给A的原型对象增加一个name:"guang"属性
    A.prototype.sayName=function(){return this.name};//给A的原型对象增加一个sayName方法
    var obj=new A();
    console.log(obj.sayName());//guang

原型

 

    // 上图展示了A 构造函数、A 的原型属性以及A 现有的实例 obj之间的关系。
    // 在此,A.prototype 指向了原型对象,而A.prototype.constructor 又指回了A。
    // 原型对象中除了包含constructor 属性之外,还包括后来添加的其他属性。A 的每个实例—
    // 都包含一个内部属性,该属性仅仅指向了 A.prototype原型对象,而构造函数没有直接的关系。
    // 虽然这 obj实例不包含属性和方法,但却可调用 A.sayName(),这是通过查找对象属性的过程来实现的。

    // 每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先
    // 从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,
    // 则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这
    // 个属性,则返回该属性的值。

    // 对于上面的例子而言,在调用A.sayName()的时候,会先后执行两次搜索
    // 首先,解析器会问:“实例A 有sayName 属性吗?”答:“没有。”然后,它继续搜索,再
    // 问:“A 的原型有sayName 属性吗?”答:“有。”于是,它就读取那个保存在原型对象中的函数。
    // 而这正是多个对象实例共享原型所保存的属性和方法的基本原理。
    /**
     * 原型链
     * 每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
     * 当一个原型对象是另一个原型对象的实例时,该原型对象将包含一个指向另一个原型的指针。相应地,另一个原型中也包含着一个指向
     * 另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条
     */

    function SuperType(){
        this.property = true;
    }
    SuperType.prototype.getSuperValue = function(){
            return this.property;
    };
    function SubType(){
        this.subproperty = false;
    }
    //继承了SuperType
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function (){
        return this.subproperty;
    };
    var instance = new SubType();
    console.log(instance.getSuperValue()); //true
      console.log(instance.constructor == SubType,instance.constructor == SuperType); //false true
//以上代码定义两个类型:SuperType、SubType。通过创建 SuperType的实例,原型对象 SubType.prototype 继承了SuperType的属性
//本质上是通过重写原型对象实现继承。如图:

 

原型链

 

// 在上面的代码中,并没有使用SubType 默认提供的原型,而是给它换了一个新原型;这个新原型
// 就是SuperType 的实例。于是,新原型不仅具有作为一个SuperType 的实例所拥有的全部属性和方法,
// 而且其内部还有一个指针,指向了SuperType 的原型。
// 最终结果就是这样的:instance 指向SubType的原型, SubType 的原型又指向SuperType 的原型。
// getSuperValue() 方法仍然还在 SuperType.prototype 中,但property 则位于SubType.prototype 中。
// 这是因为property 是一个实例属性,而getSuperValue()则是一个原型方法。既然SubType.prototype 现在是SuperType
// 的实例,那么property 当然就位于该实例中了。此外,要注意instance.constructor 现在指向的
// 是SuperType,这是因为SubType 的原型指向了另一个对象——>SuperType 的原型,而这个原型对象的constructor 属性指向的是SuperType。
// 在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。就拿上面的例子来说,调用
// instance.getSuperValue()会经历三个搜索步骤:1>搜索实例;2>搜索SubType.prototype;3>搜索SuperType.prototype,
// 4>最后一步才会找到该方法。在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。

 

// 事实上,因为所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。
// 因此默认原型都会包含一个内部指针,指向Object.prototype。这也正是所有自定义类型都会继承toString()、valueOf()等方法的根本原因。对该例子来说完整的原型链应该如下:

 

完整的原型链

 

posted @ 2018-11-05 22:43  ノ→_→(←_←  阅读(216)  评论(0编辑  收藏  举报