JS原型及原型链


原图地址: Javascript Object Hierarchy (mollypages.org)
在这里插入图片描述

prototype

每个函数都有一个prototype

当你创建函数时,JS会为这个函数自动添加prototype属性,值是一个有 constructor 属性(指向这个函数本身)的对象,不是空对象。而一旦你把这个函数当作构造函数(constructor)调用(即通过new关键字调用),那么JS就会帮你创建该构造函数的实例,实例继承构造函数prototype的所有属性和方法(实例通过设置自己的__proto__指向承构造函数的prototype来实现这种继承)

function Person() {}

console.log(Person.prototype);  // {constructor: ƒ...}

console.log(Person.prototype.constructor === Person); // true

在这里插入图片描述

对象__proto__

每个对象都有一个__proto__

function Person() {}
Person.prototype.name = 'zxx';
Person.prototype.sayName = function() {
  console.log(this.name);
}
var person1 = new Person();
person1.sayName();  // zxx 

每个对象都有一个隐藏的属性__proto__,这个属性指向创建它的构造函数的原型对象。即:

person1.__proto__ === Person.prototype

__proto__是一个隐藏的属性,且不是一个规范属性,该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它。__proto__属性已在ECMAScript 6语言规范中标准化,用于确保Web浏览器的兼容性,因此它未来将被支持。它已被不推荐使用,现在更推荐使用Object.getPrototypeOf/Reflect.getPrototypeOfObject.setPrototypeOf/Reflect.setPrototypeOf

var obj = {};
obj.__proto__ === Object.getPrototypeOf(obj); // true

看obj.__proto__里面有什么:
在这里插入图片描述

表面上看,上图的对象里存在一个__proto__ 属性。实际上,它只是开发者工具为了方便让开发者查看原型,故意渲染出来的虚拟节点。虽然跟对象的其它属性并列,但并不在该对象中。

__proto__属性既不能被 for in 遍历出来,也不能被 Object.keys(obj) 查找出来。

访问对象的 obj.__proto__属性,默认走的是 Object.prototype 对象上__proto__属性的 get/set 方法。

Object.defineProperty(Object.prototype, '__proto__', {
    get() {
        console.log('get __proto__');
    }
});

var obj = {};
console.log(obj.__proto__);  // get __proto__

在这里插入图片描述
通过覆盖 Object.prototype.__proto__我们可以看到,访问普通对象的__proto__触发了 Object.prototype 上的 __proto__的 get 方法。

因此,普通对象创建时,只需要将它内部的隐式引用指向 Object.prototype 对象,就能兼容__proto__ 属性访问行为,不需要将原型隐式挂载到对象的__proto__属性。

总结: 每个对象都有一个__proto__属性,指向创建该对象的函数的prototype,但Object.prototype确实一个特例——它的__proto__指向的是null

Object.prototype.__proto__ === null;  // true

原型链

function Person() {}
Person.prototype.name = 'zxx';
Person.prototype.sayName = function() {
  console.log(this.name);
}
var person1 = new Person();

person1.sayName();  // zxx 

以上代码中,person1是Person函数new出来的对象,person1.sayName方法是怎么来的呢?——从Person.prototype得来,因为person1.__proto__指向的是Person.prototype

访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。

操作符instanceof正是通过探测obj的__proto__的__proto__ … === Constructor.prototype来验证obj是否是Constructor的实例

以下代码是模拟instanceof

function instanceofFn(left, right) {
  if (left === null || typeof left !== "object") {
    return false;
  }
  // 获得右边对象的prototype
  var prototype = right.prototype;
  // 获得左边对象的原型(内部[[Prototype]]属性的值)
  var proto = Object.getPrototypeOf(right);
  while (true) {
    if (proto === null) {
      return false;
    }
    if (proto === prototype) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }
}

Object.prototype是原型链的顶端,所有对象从它继承了包括toString等方法和属性。

Object本身是构造函数,继承了Function.prototype

Function也是对象,继承了Object.prototype。这里就有一个鸡和蛋的问题:

Object instanceof Function // true
Function instanceof Object // true

看ES规范是怎么说的?

Function本身就是函数,Function.__proto__是标准的内置对象Function.prototype。
Function.prototype.__proto__是标准的内置对象Object.prototype。

// 原型链到Object.prototype终止
console.log(Object.prototype.__proto__ === null);  // true

// Function.prototype直接继承Object.prototype
console.log(Function.prototype.__proto__ === Object.prototype); // true

// Object/Array/String等等构造函数本质上和Function一样,均继承于Function.prototype。
console.log(Function.prototype === Function.__proto__); // true
console.log(Function.prototype === Array.__proto__); // true
console.log(Function.prototype === Object.__proto__); // true

总结: 先有Object.prototype(原型链顶端),Function.prototype继承Object.prototype而产生,最后,Function和Object和其它构造函数继承Function.prototype而产生

参考资料:

深入理解 JavaScript 原型

从__proto__和prototype来深入理解JS对象和原型链

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值