您的位置:首页 > 移动开发 > Objective-C

Object.prototype.__proto__, [[prototype]] 和 prototype

2020-08-15 20:34 691 查看 https://www.cnblogs.com/ayuuuu

Object.prototype.__proto__
是什么?

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

      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.protype 是任何函数的原型, 比如 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]]
关联了起来, 形成了一个链式结构 --- 原型链. 如果没把例子举起来, 是我不会讲故事, 请点这里)看图理解.

为什么不推荐使用它?

  1. 虽然所有现代浏览器都实现了该访问器属性. ES6 (ECMA2015) 及之后的标准也暂时包含了它, 它的存在只是为了确保规范与浏览器兼容
  2. 操作
    [[prototype]]
    属性 (只要该属性变更了), 各个浏览器引擎针对
    prototype
    相关的优化会失效, 这就导致访问原型上的属性很慢

如果不得不使用呢?

  1. 推荐使用

    Object.getPrototypeOf()
    方法代替
    Object.prototype.__proto__

  2. 虽然原型只是对象,但它们由 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 为对应的包装对象, 所以只有
      null
      undefined
      不能访问到
      Object.prototype.__proto__

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

    众所周知,

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

    这张图说明了 JavaScript 的继承 (委托):

    new Foobar
    ---
    __proto__
    --->
    Foobar.prototype
    ---
    __proto__
    --->
    Object.prototype
    ---
    __proto__
    --->
    null
    .

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

  • 内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐