zoukankan      html  css  js  c++  java
  • Object.prototype.__proto__, [[prototype]] 和 prototype

    Object.prototype.__proto__ 是什么?

    1. __proto__ 是一个访问器属性, 用于访问对象的原型 [[prototype]] (见以下模拟的 gettersetter 方法, 不一定完全与规范一致, 仅供参考)

      • get Object.prototype.__proto__

        get __proto__() {
            // Let O be ? ToObject(this value).
            if(this === void(0) || this === null) {
                throw TypeError(`Cannot read property '__proto__' of ${this}`);
            }
            let O = Object(this);  // this !== null 或 undefined 时, Return ! ToObject(value);
            // Return ? O.[[GetPrototypeOf]]().
            return Object.getPrototypeOf(O);
        }
        
      • set Object.prototype.__proto__

        set __proto__(proto) {
            // Let O be ? RequireObjectCoercible(this value).
            if(this === void(0) || this === null) {
                throw TypeError(`Cannot set property '__proto__' of ${this}`);
            }
            let O = this;  // this !== null 或 undefined 时, return argument;
            // If Type(proto) is neither Object nor Null, return undefined.
            if (typeof proto !== 'object') {  // typeof null === 'object'
                return;
            }
            // If Type(O) is not Object, return undefined.
            if (typeof O !== 'object') {  // O !== null 或 undefined 
                return;
            }
            // Let status be ? O.[[SetPrototypeOf]](proto).
            // If status is false, throw a TypeError exception.
            // Return undefined.
            Object.setPrototypeOf(O, proto);
            return;
        }
        
    2. 通过它可以访问到对象的 [[prototype]], 也即对象的原型

    3. [[prototype]] 的值是该对象的原型或 null (对于 Object.prototype 对象而言, 其没有原型, 返回null: Object.prototype.__proto__; // null)

    [[prototype]]prototype 的关系

    举个例子 (一定要举起来啊!):

    class Person {
        constrctor(name, age) {
            this.name = name;
        }
    }
    
    let p1 = new Person('ayu');
    
    // 对于实例 p1 来说, 它的原型 [[prototype]] 是 Person 对象的 prototype 属性值. 也即实例 p1 的原型是 Person.prototype
    Object.getPrototypeOf(p1) === Person.prototype;  // true
    
    // 顺便再说下 constructor
    // 实例由原型中的 constructor 属性值构造, 也即实例 p1 由 Person (Object.getPrototypeOf(p).constructor) 构造
    Object.getPrototypeOf(p1).constructor === Person;  // true
    p1.constructor === Person;  // true
    // 实例与 constructor 的关系为 n : 1, 因此每个实例的构造器均指向 Person
    let p2 = new Person('ayu2');
    p1.constructor === p2.constructor;  // true
    // 任何函数都是由 Function 构造的, 比如 Object, Person 等, 比较特殊的是: Function.constructor === Function
    Person.constructor === Function;  // true
    Function.constructor === Function;  // true
    
    // 再来说下原型链
    // Function.prototype 是任何函数的原型, 比如 Object, Person 等, 比较特殊的是 Object.getPrototypeOf(Function) === Function.prototype
    Object.getPrototypeOf(Person) === Function.prototype;  // true
    Object.getPrototypeOf(Function) === Function.prototype;  // true
    // 最后, 任何原型的原型最终都追溯到 Object.prototype 或 null. 这形成了一个链式结构, 它被叫做原型链
    Object.getPrototypeOf(Person.prototype) === Object.prototype;  // true
    Object.getPrototypeOf(Object.getPrototypeOf(Person)) === Object.prototype;  // true
    Object.getPrototypeOf(Function.prototype) === Object.prototype;  // true
    Object.getPrototypeOf(Object.getPrototypeOf(Object)) === Object.prototype;  // true
    Object.getPrototypeOf(Object.prototype) === null;  // true
    

    综上, [[prototype]] 表示了一个实例的原型 (prototype 属性的值表示了其实例的原型对象), 对象与对象之间通过 [[prototype]] 关联了起来, 形成了一个链式结构 --- 原型链. 如果没把例子举起来, 是我不会讲故事, 请点这里)看图理解.

    [为什么不推荐使用 Object.prototype.__proto__?]

    1. 虽然所有现代浏览器都实现了该访问器属性, ES6 (ECMA2015) 及之后的标准也暂时包含了它, 但是它的存在只是为了确保规范与浏览器兼容

    如果不得不使用 Object.prototype.__proto__ 呢?

    1. 推荐使用 Object.getPrototypeOf() 方法代替 Object.prototype.__proto__

    为什么不推荐修改 [[prototype]]?

    1. 操作 [[prototype]] 属性 (只要该属性变更了), 各个浏览器引擎针对 prototype 相关的优化(缓存)会失效, 这就导致访问原型上的属性很慢

    如果不得不修改 [[prototype]] 呢?

    1. 虽然原型只是对象,但它们由 JavaScript 引擎专门处理,以优化在原型上查找方法的性能表现。把你的原型放在一旁!或者,如果你确实需要修改原型,请在其他代码运行之前执行此操作,这样至少不会让引擎所做的优化付诸东流。

    JavaScript 中谁不能访问到 Object.prototype.__proto__?

    • 原型链上没有 Object.prototype 对象的对象, 均不能访问
      • 比如使用 Object.create(null) 创建的对象或我们变更了其原型的对象 obj.__proto__ = null, 该类对象不能访问 Object.prototype.__proto__ (但我们可以通过 Object.getPrototypeOf(obj) 访问其原型: Object.getPrototypeOf(Object.create(null)); // null)
    • 没有原型的原始值
      • 一般来说, null, undefined, number, string, boolean, symbol, bigint 这些基本数据类型的 (原始) 值没有原型 (Object.getPrototypeOf(null); // TypeError: Cannot convert undefined or null to object, 原始值不可能有原型), 所以其无法访问到 Object.prototype.__proto__. 但鉴于除了 null, undefined 以外的基本数据类型值在运算时会自动装箱 autoboxing 为对应的包装对象, 所以只有 nullundefined 不能访问到Object.prototype.__proto__

    Object.prototype.__proto__ 的值是 null, 然后呢?

    众所周知, Object.prototype.__proto__ 的值是 null, 通常来说也是一个对象的原型链终点, 它表示了 Object.prototype 对象没有原型. 附一张图 (图片来源于这里):

    prototype

    这张图说明了 JavaScript 的继承 (委托): new Foobar --- __proto__ ---> Foobar.prototype --- __proto__ ---> Object.prototype --- __proto__ ---> null.

    (用《你不知道的JavaScript》里的话来说: 继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些.)

  • 相关阅读:
    mysqldump 导出数据库为DBname的表名为Tname的表结构 导出数据库的所有表的表结构
    mysqldump 备份某张表 Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions,
    nfs missing codepage or helper program, or other error
    date 增加一个小时 减少一个小时
    mysqldump 备份单个数据库
    mysql删除账户
    怎么删除某个用户的所有帖子?
    mongodb删除重复数据
    ReSharper2018破解详细方法
    激活windows和office
  • 原文地址:https://www.cnblogs.com/ayuuuuuu/p/13510214.html
Copyright © 2011-2022 走看看