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.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 为对应的包装对象, 所以只有 nullundefined 不能访问到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 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些.)

Object.prototype.__proto__, [[prototype]] 和 prototype的更多相关文章

  1. Javascript中Function,Object,Prototypes,__proto__等概念详解

    http://anykoro.sinaapp.com/2012/01/31/javascript%E4%B8%ADfunctionobjectprototypes__proto__%E7%AD%89% ...

  2. JavaScript:Function/Object/prototype/__proto__

    console.log(Object.__proto__===Function.prototype); //true console.log(Object.prototype.__proto__); ...

  3. Object & prototype & __proto__ All In One

    Object & prototype & proto All In One js 原型,原型链,原型对象 const obj ={}; // {} const obj = new Ob ...

  4. js & object & prototype & __proto__ & prototype chain

    js & object & prototype & proto & prototype chain constructor prototype === instance ...

  5. 关于 JavaScript prototype __proto__ 一点总结

    http://www.cnblogs.com/wbin91/p/5265163.html 先上代码 function(y) Foo{ this.y = y;} Foo.prototype.x = 10 ...

  6. javascript prototype __proto__区别

    An Object's __proto__ property references the same object as its internal [[Prototype]] (often refer ...

  7. Javascript Prototype __proto__ constructor 三者的关系

    JavaScript三大毒瘤 --- this,原型链,作用域 在我等菜鸟一步一步升级中的过程中,这三个概念总是困扰这我们(可能只有我吧,我比较蠢).这三个东西往往都很绕,今天我就来分享一下我对原型. ...

  8. 深入理解JavaScript原型:prototype,__proto__和constructor

    JavaScript语言的原型是前端开发者必须掌握的要点之一,但在使用原型时往往只关注了语法,其深层的原理并未理解透彻.本文结合笔者开发工作中遇到的问题详细讲解JavaScript原型的几个关键概念, ...

  9. 基础知识点 关于 prototype __proto__

    基础知识点 关于 prototype  __proto__ 供js新手参考 JavaScript 的一些基础知识点: 在 JavaScript 中,所有对象 o 都拥有一个隐藏的原型对象(在 Fire ...

随机推荐

  1. three.js 数学方法之Plane

    今天郭先生就来继续说一说three.js数学方法中的plane(平面).在三维空间中无限延伸的二维平面,平面方程用单位长度的法向量和常数表示.构造器为Plane( normal : Vector3, ...

  2. 题解 洛谷 P4171 【[JSOI2010]满汉全席】

    考虑\(2-SAT\). 将汉式看作\(0\)状态,满式看做\(1\)状态,将每个材料拆成\(01\)两个状态. 从\(a\)向\(b\)连有向边表示的意义为选了\(a\)后必须选\(b\). 那么每 ...

  3. 五分钟快速搭建 Serverless 免费邮件服务

    1. 引言 本文将带你快速基于 Azure Function 和 SendGrid 构建一个免费的Serverless(无服务器)的邮件发送服务,让你感受下Serverless的强大之处. 该服务可以 ...

  4. 关于docker--详解安装,常规操作,导入导出等(2017-3-29)

    测试环境 :CentOS 7.1 64位 目的:展示docker的常规使用(安装,常规操作,导入导出等) 其他:关于原理等请参考文章后面的延伸阅读,本文不做深入探讨,且方法不唯一 0x01 关于安装d ...

  5. java基础(11)--封装

    一.java面向对象三大特别: 1.封装 2.继承 3.多态   二.封装的作用 1.属性私有化(private) 2.对外提供简单的入口 如公开的set()与get()方法,并且都不带static ...

  6. form表单两种提交方式的不同

      我们在使用<Form>表单的时候,最常用的提交方式就是Get和Post.我们都知道这两种方式最大的差别就是安全性,除此之外,它们还有哪些其他的区别,你知道吗?   在<Form& ...

  7. MacOS安装npm

    原文链接:https://www.cnblogs.com/zmdComeOn/p/11543441.html 第一步:检查brew -v是否安装了homebrew这个MacOS 缺失的软件包的管理器. ...

  8. zookeeper 源码编译

    环境:mac 1.github上下载 源码 项目地址:https://github.com/apache/zookeeper 2.安装 ant mac:brew update ->  brew ...

  9. PHP link() 函数

    定义和用法 link() 函数创建一个从指定名称连接的现存目标文件开始的硬连接. 如果成功,该函数返回 TRUE.如果失败,则返回 FALSE. 语法 link(target,link) 参数 描述 ...

  10. Skill 脚本演示 ycCommonCenterMos.skl

    https://www.cnblogs.com/yeungchie/ ycCommonCenterMos.skl 自动生成一个共质心差分对 Mos ,可以自定布局类型. 回到目录