# [[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);

img

# 只有函数和类有 prototype 属性。

使用 typeof 检测一个类的标识符,返回值为 'function',从这儿可以看出,类本质上就是 “构造函数”。因为类是函数,函数又是对象,所以,类也是对象。

综上,函数和类既有 [[Prototype]] 属性,又有 prototype 属性。(也有例外,如 Proxy 没有 prototype 属性)

例如,以下代码的执行结果:

function fn() { };
console.dir(fn);
class Clazz { };
console.dir(Clazz);
console.log(typeof Clazz);  // 输出结果为:function

img

# 原型链 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 上的方法),除此之外,没有方法可以使用。

# js 的完整原型链示意图

js的完整原型链示意图