1.prototype是什么
prototype是函数的特殊属性,这个属性是函数所独有的(即其他类型不会有这个属性)。该特殊属性可与 JavaScript 的 new 操作符一起使用。
function A(){
}
let b = "我是个字符串";
let c = new A();
class D {
}
// 尝试输出该属性
console.log(A.prototype); // {constructor: ƒ}
console.log(b.prototype); // undefined
console.log(c.prototype); // undefined
console.log(D.prototype); // {constructor: ƒ}
可见只有函数才有该属性。
张三:睁眼说瞎话?上面D不是也有输出吗?
我:class其实是模拟出的 “类”,只是个语法糖,仍然基于原型,它也是一个函数对象。
张三:哦,行吧!
2.__proto__是什么
每个实例对象(object)都有一个私有属性__proto__或者称为[[Prototype]],指向它的构造函数的原型对象(prototype)。注意这个属性是每个object都有的。
function A(){
}
let b = "我是个字符串";
let c = new A();
// 输出该属性
console.log(b.__proto__); // String {'', constructor: ƒ, anchor: ƒ, big: ƒ, blink: ƒ, …}
console.log(c.__proto__); // {constructor: ƒ}
console.log(c.__proto__ === A.prototype); // true
// 将prototype展开的构造函数就是A本身也是c的构造函数
A.prototype.constructor === A === c.constructor
根据__proto__定义可知,c.__proto __ === c.constructor.prototype === A.prototype
通常我们所说的原型链就是通过这个属性来访问的。
下面引用MDN的例子解释说明(o.[[Prototype]]就是o.__proto __)
// 让我们从一个函数里创建一个对象 o,它自身拥有属性 a 和 b 的:
let f = function () {
this.a = 1;
this.b = 2;
}
let o = new f(); // {a: 1, b: 2}
// 在 f 函数的原型上定义属性
f.prototype.b = 3;
f.prototype.c = 4;
// 不要在 f 函数的原型上直接定义 f.prototype = {b:3,c:4};这样会直接打破原型链
// o.[[Prototype]] 有属性 b 和 c
// (其实就是 o.__proto__ 或者 o.constructor.prototype)
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最后 o.[[Prototype]].[[Prototype]].[[Prototype]] 是 null
// 这就是原型链的末尾,即 null,
// 根据定义,null 就是没有 [[Prototype]]。
// 综上,整个原型链如下:
// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null
console.log(o.a); // 1
// a 是 o 的自身属性吗?是的,该属性的值为 1
console.log(o.b); // 2
// b 是 o 的自身属性吗?是的,该属性的值为 2
// 原型上也有一个'b'属性,但是它不会被访问到。
// 这种情况被称为"属性遮蔽 (property shadowing)"
console.log(o.c); // 4
// c 是 o 的自身属性吗?不是,那看看它的原型上有没有
// c 是 o.[[Prototype]] 的属性吗?是的,该属性的值为 4
console.log(o.d); // undefined
// d 是 o 的自身属性吗?不是,那看看它的原型上有没有
// d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
// o.[[Prototype]].[[Prototype]] 为 null,停止搜索
// 找不到 d 属性,返回 undefined
从上述例子可知原型链就是通过__proto__一层一层往上搜索形成的链路,直到顶层Object的__proto__为null止。
总结
- prototype属性是函数独有的,指代其原型对象,可以与new操作符搭配创建实例。当遇到xxx.prototype时,其实就是获取xxx的原型对象。
- __proto__属性是每个实例对象都拥有的,指向其构造函数的原型对象(即构造函数的prototype)。 xxx.__proto __ === xxx.constructor.prototype