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

//以上代码定义两个类型: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 的实例,
// 因此默认原型都会包含一个内部指针,指向Object.prototype。这也正是所有自定义类型都会继承toString()、valueOf()等
// 默认方法的根本原因。对该例子来说完整的原型链应该如下:

完整的原型链

//内置函数及其原型链

内置函数及其原型链

//上述图中的原型及原型链的关系图中, 将 String 换成 Array、Number、Boolean、及其他一些内置函数时,依然有类似的关系
//下面是与之相关的一些运行结果:

console.log("XXX");
console.log("String:",String);//String: function String() 内置构造函数
console.log("Function:",Function);//Function: function Function() 内置构造函数
console.log("Object:",Object);//Object: function Object() 内置构造函数
console.log("Array:",Array);//Array: function Array() 内置构造函数
console.log("Number:",Number);//Number: function Number() 内置构造函数
console.log("Boolean:",Boolean);//Boolean: function Boolean() 内置构造函数


console.log("XXX.prototype");
console.log("String.prototype:",String.prototype);//String.prototype: String { "" } 空字符串
console.log("Function.prototype:",Function.prototype);//Function.prototype: function () 空函数
console.log("Object.prototype:",Object.prototype);//Object.prototype: Object { … } 空对象
console.log("Array.prototype:",Array.prototype);//Array.prototype: Array [] 空数组
console.log("Number.prototype:",Number.prototype);//Number.prototype: Number { 0 } 数字零
console.log("Boolean.prototype:",Boolean.prototype);//Boolean.prototype: Boolean { false } 布尔值 false


console.log("XXX.prototype.constructor");
console.log("String.prototype.constructor:",String.prototype.constructor);
//String.prototype.constructor: function String() 内置构造函数
console.log("Function.prototype.constructor:",Function.prototype.constructor);
//Function.prototype.constructor: function Function() 内置构造函数
console.log("Object.prototype.constructor:",Object.prototype.constructor);
//Object.prototype.constructor: function Object() 内置构造函数
console.log("Array.prototype.constructor:",Array.prototype.constructor);
//Array.prototype.constructor: function Array() 内置构造函数
console.log("Number.prototype.constructor:",Number.prototype.constructor);
//Number.prototype.constructor: function Number() 内置构造函数
console.log("Boolean.prototype.constructor:",Boolean.prototype.constructor);
//Boolean.prototype.constructor: function Boolean() 内置构造函数


console.log("XXX.__proto__");
console.log("String.__proto__:",String.__proto__);//String.__proto__: function () 空函数
console.log("Function.__proto__:",Function.__proto__);//Function.__proto__: function () 空函数
console.log("Object.__proto__:",Object.__proto__);//Object.__proto__: function () 空函数
console.log("Array.__proto__:",Array.__proto__);//Array.__proto__: function () 空函数
console.log("Number.__proto__:",Number.__proto__);//Number.__proto__: function () 空函数
console.log("Boolean.__proto__:",Boolean.__proto__);//Boolean.__proto__: function () 空函数


console.log("XXX.prototype.__proto__");
console.log("String.prototype.__proto__:",String.prototype.__proto__);
//String.prototype.__proto__: Object { … } 空对象
console.log("Function.prototype.__proto__:",Function.prototype.__proto__);
//Function.prototype.__proto__: Object { … } 空对象
console.log("Object.prototype.__proto__:",Object.prototype.__proto__);
//Object.prototype.__proto__: null 空对象
console.log("Array.prototype.__proto__:",Array.prototype.__proto__);
//Array.prototype.__proto__: Object { … } 空对象
console.log("Number.prototype.__proto__:",Number.prototype.__proto__);
//Number.prototype.__proto__: Object { … } 空对象
console.log("Boolean.prototype.__proto__:",Boolean.prototype.__proto__);
//Boolean.prototype.__proto__: Object { … } 空对象


console.log("self defined function");
function test(){}
console.log("test:",test);//test: function test() 自定义函数
console.log("test.prototype:",test.prototype);//test.prototype: Object { … } 空对象
console.log("test.prototype.constructor:",test.prototype.constructor);
//test.prototype.constructor: function test() 自定义函数
console.log("test.__proto__:",test.__proto__);//test.__proto__: function () 空函数
console.log("test.prototype.__proto__:",test.prototype.__proto__);
//test.prototype.__proto__: Object { … } 空对象

猜你喜欢

转载自www.cnblogs.com/go4it/p/9906306.html