zoukankan      html  css  js  c++  java
  • [[Prototype]] and Prototype

    经常会谈论到prototype,实例对象的prototype对象和function的prototype属性是两个不同的概念,但是经常被初学者混淆。

    这儿用实例对象表示通过对应function创建的object,是相对于对应的constructor而言的。在ES中没有实例对象这个说法。

    • 实例对象的prototype对象,一般称之为原型对象,内部属性[[Prototype]](一般用[[]]表示内部属性)指向此对象,不过通常[[Prototype]]不能直接被访问,部分浏览器提供非标准的__proto__,可以它可以访问原型对象
    • function的prototype属性可以直接访问。使用function创建的实例,实例的[Prototype]属性即指向prototype属性指向的对象。

    以function A 和 A的实例a为例

    function A () { }
    A.prototype.x = 2;
    
    var a = new A();
    
    // Sometimes a.[[Prototype]] == A.prototype
    // a.[[Prototype]] ---> Prototype <--- A.prototype
    alert(a.x); // 2
    
    // modify A.prototype
    A.prototype = {
        y: 3,
        z: 4,
        method: function () {
            alert(this);
        }
    };
    
    // 当A.prototype被更改后,与a.[[Prototype]]不再指向同一个对象
    // a.[[Prototype]] 仍然指向旧的对象
    alert(a.y); // undefined
    alert(a.x); // a.[[Prototype]]仍然指向旧的原型对象
    
    var b = new A();
    
    alert(b.x); // undefined 新的A.prototype上未定义x属性
    alert(b.y); // 3

    [[Prototype]] 和 prototype 的关系


    这儿的prototype指function的prototype属性。当然function作为实例对象(Function的实例)也有[[Prototype]]属性,不过这儿不讨论它

    在使用函数创建对象时(new A())实际上是调用了函数的[[constructor]]属性,规范中定义了[[Constructor]]属性被调用后的内部过程表述如下:

    When the [[Construct]] property for a Function object F is called, the following steps are taken:
    1. Create a new native ECMAScript object.
    2. Set the [[Class]] property of Result(1) to “Object”.
    3. Get the value of the prototype property of F.
    4. If Result(3) is an object, set the [[Prototype]] property of Result(1) to Result(3).
    5. If Result(3) is not an object, set the [[Prototype]] property of Result(1) to the original Object prototype object as described in 15.2.3.1.
    6. Invoke the [[Call]] property of F, providing Result(1) as the this value and providing the argument list passed into [[Construct]] as the argument values.
    7. If Type(Result(6)) is Object then return Result(6).
    8. Return Result(1).

    用伪代码可以表示如下:

    F.[[Construct]](initialParameters):
    
    O = new NativeObject();
    
    // 设置对象的[[Class]] 属性为"Object" 
    O.[[Class]] = "Object"
    
    // 获取当前时刻F.prototype的指向,注意是当前时刻的,因为F.prototype以后可能会改变
    var __objectPrototype = F.prototype;
    
    // if __objectPrototype is an object, then:
    O.[[Prototype]] = __objectPrototype
    // else:
    O.[[Prototype]] = Object.prototype;
    // where O.[[Prototype]] is the prototype of the object
    
    // initialization of the newly created object
    // applying the F.[[Call]]; pass:
    // as this value – newly created object - O,
    // arguments are the same as initialParameters for F
    R = F.[[Call]](initialParameters); this === O;
    // where R is the returned value of the [[Call]]
    // in JS view it looks like:
    // R = F.apply(O, initialParameters);
    
    // 如果调用[[Call]]的返回值是个对象则返回R,否则返回上面创建的对象O
    // if R is an object
    return R
    // else
    return O

    我们有这样的结论:

    • [[Prototype]]是实例对象的属性,prototype是function的属性
    • [[Prototype]]是在对象被创建时确定的
    • [[Prototype]]在对象刚创建时与prototype指向同一个对象
    • prototype可以被更改,在更改之后将于之前通过该function创建的实例对象的[[Prototype]]不再指向同一个对象
    • 如果function中显式的return一个对象,则使用new调用构造函数时返回的是return的对象,而不是上面创建的NativeObject的对象

    关于最后一点可以看一个示例:

    function A () {
        this.x = 1;
        this.y = 2;
        return {
            x: this.x
        };
    }
    
    // 这儿相当于 a = A.apply(O); 其中O是创建的NativeObject对象
    // this.x === O.x
    var a = new A();
    
    alert(a.x); // 1
    alert(a.y); // undefined 没有返回y,不能访问O.y

    __prototype__


    部分Ecmascript的实现提供__proto__可以访问实例对象的内部属性[[Prototype]],如chrome,ff,safari等(具体从什么版本开始支持,没有考证过)

    function A () { }
    A.prototype.x = 2;
    
    var a = new A();
    
    var __newPrototype = {
        y: 3,
        z: 4,
        method: function () {
            alert(this);
        }
    };
    
    A.prototype = __newPrototype;
    
    var b = new A();
    
    alert(b.x);
    alert(b.y); // 3
    alert(a.y); // undefined
    
    a.__proto__ = __newPrototype;
    
    alert(a.x); // undefined
    alert(a.y); // 3
    
    A.prototype.y = 0;
    alert(a.y); // 0
    alert(b.y); // 0

    上面示例通过__proto__修改了[[Prototype]], 在将原有对象实例的[[Prototype]]指向function的新prototype之后,原有对象实例和新对象实例的[[Prototype]]相同了。

    需要注意一点__protp__不是规范中定义的standard属性,只被部分浏览器支持,使用时要慎重。

    ES5中引入了Object.getPrototypeOf(O)方法,它可以直接返回一个对象的[[Protptype]],不过与__proto__不同,这个返回值是只读的,我们不能通过它对原型对象设置和更改属性。

     
  • 相关阅读:
    用C++实现斐波那契数列
    用C++实现的八皇后问题
    用C++实现的元胞自动机
    用C++实现的贪吃蛇游戏
    用C++实现的解数独(Sudoku)程序
    蓝桥杯-3.4
    汇编——实验9
    汇编——实验5
    汇编——实验4
    汇编——实验3
  • 原文地址:https://www.cnblogs.com/zhangjie/p/3069263.html
Copyright © 2011-2022 走看看