JavaScript: prototype、__proto__、[[prototype]]的区别
prototype、__proto__、[[prototype]] 三者之间的区别
最近在网上看到 instanceof
的手写版,里面涉及到了 prototype
、__proto__
,仔细推敲 一下代码,发现自己对 prototype
, __proto__
的理解不够深刻导致自己对手写版的 instanceof 代码理解不是很透彻。
首先来看一下 instanceof
的手写代码
function myInstanceof(obj, constructor) {
// 拿到obj的
let implicitPrototype = obj?.__proto__;
const displayPrototype = constructor.prototype;
while (implicitPrototype) {
if (implicitPrototype === displayPrototype) return true;
implicitPrototype = implicitPrototype.__proto__;
}
return false;
}
如果不是很清楚 prototype
, __proto__
的话,很难正确理解上述代码。所以我们先要搞清楚它们的区别。
1. 概念区分
其实说 __proto__
并不准确,确切的说是对象的 [[prototype]]
属性,只不过在主流的浏览器中,都用 __proto__
来代表 [[prototype]]
属性,因为 [[prototype]]
只是一个标准,而针对这个标准,不同的浏览器有不同的实现方式。在ES5中用 Object.getPrototypeOf
函数获得一个对象的 [[prototype]]
。ES6中,使用 Object.setPrototypeOf
可以直接修改一个对象的 [[prototype]]
。
而 prototype
属性是只有函数才特有的属性,当你创建一个函数时,js会自动为这个函数加上 prototype
属性,值是一个空对象。所以,函数在js中是非常特殊的,是所谓的一等公民。
2.必须明确
__proto__
是隐式原型
prototype
是显式原型
显式原型 :每一个函数在创建之后都会拥有一个名为 prototype
的属性,这个属性指向函数的原型对象。
隐式原型:JavaScript中任意对象都有一个内置属性 [[prototype]]
,在ES5之前没有标准的方法访问这个内置属性,但是大多数浏览器都支持通过 __proto__
来访问。
注意: Object.prototype
这个对象是个例外,它的 __proto__
值为 null
二者的关系:
隐式原型指向创建这个对象的函数(constructor)的 prototype
3.他们的作用
隐式原型的作用:构成原型链,同样用于实现基于原型的继承。举个例子,当我们访问 obj 这个对象中的x属性时,如果在 obj 中找不到,那么就会沿着 __proto__
依次查找。
显式原型的作用:用来实现基于原型的继承与属性的共享
所以现在我们可以分析一下 instanceof
代码了。一般来说,instanceof
内部实现机制和隐式原型、显式原型有直接的关系。instanceof
的左值一般是一个对象,右值一般是一个构造函数,用来判断左值是否是右值的实例。
所以在我们的手写代码中:
//设 obj instanceof constructor
//通过判断
obj.__proto__.__proto__ ..... === constructor.prototype
//最终返回true or false
也就是一直沿着obj的 __proto__
一直寻找下去,知道等于 constructor.prototype
或者 null
为止。
这就是instanceof的原理
4.进一步的探究
console.log(Object instanceof Function); //true
console.log(Function instanceof Object); //true
上面两行代码的结果都为 true,这就有点奇怪了
接下来我们来探究一下:
console.log(Object.prototype.__proto__); //null
所以原型链的尽头(root)是 Object.prototype
。所有对象均从 Object.prototype
继承属性。
console.log(Function.prototype);
console.log(Function.__proto__);
console.log(Function.prototype === Function.__proto__); //true
所以 Function.prototype
和 Function.__proto__
为同一对象。
console.log(Function.prototype === Array.__proto__); //true
console.log(Function.prototype === String.__proto__); //true
console.log(Function.prototype === Object.__proto__); //true
console.log(Function.prototype === Number.__proto__); //true
console.log(Function.prototype === Boolean.__proto__); //true
这意味着: Object
/Array
/String
等等构造函数本质上和 Function
一样,均继承于 Function.prototype
。
console.log(Function.prototype.__proto__ === Object.prototype); //true
最后 Function.prototype
直接继承 Object.prototype
总结:先有 Object.prototype
(原型链顶端), Function.prototype
继承 Object.prototype
而产生,最后,Function
和 Object
和其它构造函数继承 Function.prototype
而产生。