# [[Prototype]] 与 prototype
[[Prototype]] 与 prototype 是不同的:
# 凡是被两层方括号括起来的属性都是内部属性,内部属性不能通过 “对象。属性” 的方式获取。
[[Prototype]] 就是内部属性,它不能通过 “对象.[[Prototype]]” 的方式获取,但可以通过 “对象.proto” 的方式获取。
prototype 是一般属性,可以直接通过 “对象.prototype” 的方式获取。
# 每个对象都有 [[Prototype]] 属性。
我们知道,数组可以通过 new Array () 创建、函数可以通过 new Function () 创建,数组和函数本质上就是对象,因此它们也有 [[Prototype]] 属性。
例如,以下代码的执行结果:
const obj = { }; | |
console.dir(obj); | |
const arr = [ ]; | |
console.dir(arr); |
# 只有函数和类有 prototype 属性。
使用 typeof 检测一个类的标识符,返回值为 'function',从这儿可以看出,类本质上就是 “构造函数”。因为类是函数,函数又是对象,所以,类也是对象。
综上,函数和类既有 [[Prototype]] 属性,又有 prototype 属性。(也有例外,如 Proxy 没有 prototype 属性)
例如,以下代码的执行结果:
function fn() { }; | |
console.dir(fn); | |
class Clazz { }; | |
console.dir(Clazz); | |
console.log(typeof Clazz); // 输出结果为:function |
# 原型链 prototype chain
最简单的原型链:
const o = { }; | |
console.log(o.__proto__ === Object.prototype); // 输出结果为:true | |
console.log(o.__proto__.__proto__); // 输出结果为:null。这说明 Object.prototype 是原型链的最末端。 |
toString、valueOf 就定义在 Object.prototype 上。
难一点的原型链:
class Person { }; | |
const p = new Person(); | |
console.log(p.__proto__ === Person.prototype); // 输出结果为:true | |
console.log(p.__proto__.__proto__ === Object.prototype); // 输出结果为:true |
Person 是一个类,也即是(构造)函数,因此 Person 是 Function 的一个实例对象(相当于 Function 是类,Person 是 Function 的实例对象),因此,另一条原型链:
console.log(Person.__proto__ === Function.prototype); // 输出结果为:true | |
console.log(Person.__proto__.__proto__ === Object.prototype); // 输出结果为:true |
更难一点,带继承的原型链:
class Person { }; | |
class Child extends Person { } | |
const c = new Child(); | |
// 原型链一: | |
console.log(c.__proto__ === Child.prototype); // 输出结果为:true | |
console.log(c.__proto__.__proto__ === Person.prototype); // 输出结果为:true | |
console.log(c.__proto__.__proto__.__proto__ === Object.prototype);// 输出结果为:true | |
// 原型链二: | |
console.log(Child.__proto__ === Person); // 输出结果为:true | |
console.log(Child.__proto__.__proto__ === Function.prototype); // 输出结果为:true | |
console.log(Child.__proto__.__proto__.__proto__ === Object.prototype);// 输出结果为:true |
Object、Array、Function、String、Date 等都是类,有 [[Prototype]] 和 prototype 两个属性。类即是构造函数,typeof Object、typeof Array 等的返回值都是 'function'。从构造函数这个角度来看,它们和 Person 类没有区别,都是 Function 的实例对象(相当于 Function 是类,Object、Array 等是 Function 的实例对象):
console.log(Object.__proto__ === Function.prototype); // 输出结果为:true | |
console.log(Array.__proto__ === Function.prototype); // 输出结果为:true | |
console.log(Function.__proto__ === Function.prototype); // 输出结果为:true。这条特别有意思。 | |
console.log(String.__proto__ === Function.prototype); // 输出结果为:true | |
console.log(Date.__proto__ === Function.prototype); // 输出结果为:true |
call、apply、bind 等方法就定义在 Function.prototype 上。
Object、Array 等也是一个对象,对象上可以定义方法。Object.defineProperty ()、Object.assign ()、Array.from () 这样的静态方法,就是定义在 Object、Array 上的。
以数组为例,它的两条原型链:
// 原型链一: | |
const arr = [] | |
console.log(arr.__proto__ === Array.prototype) | |
console.log(arr.__proto__.__proto__ === Object.prototype) | |
// 原型链二(同 Person 完全相同): | |
console.log(Array.__proto__ === Function.prototype); // 输出结果为:true | |
console.log(Array.__proto__.__proto__ === Object.prototype);// 输出结果为:true |
数组的 push、shift、indexOf 等方法就定义在 Array.prototype 上。
数组对象,如上例中的 arr,可以使用 Array.prototype 上的方法,也可以使用 Object.prototype 上的方法,除此之外,没有方法可以使用;
Array 可以使用自己身上的方法,如 Array.from (),也可以使用 Function.prototype 上的方法,如 call ()(可以用,但一般用不到),还可以使用 Object.prototype 上的方法(数组对象与 Array 都可以使用 Object.prototype 上的方法),除此之外,没有方法可以使用。