数组方法和原型

在JS中,数组拥有各种数组方法

1
2
3
const arr = new Array(1,2,3)
console.log(arr) // -> [ 1, 2, 3 ]
console.log(arr.forEach) // -> [Function: forEach]

当控制台输出arr时,并没有输出所谓数组方法,但数组方法确实存在着

其实这些数组方法就在原型链里

1
2
const arr = new Array(1,2,3)	// -> [ 1, 2, 3 ]
console.log(arr.__proto__.forEach) // -> [Function: forEach]

原型

prototype是在函数上的一个属性,它是一个对象,被称为原型/原型对象

创建一个函数时,它会被默认添加上一个prototype属性

1
2
function fun () {}
console.log(fun.prototype) // -> {}

__proto__是在对象上的一个属性,它指向了该对象的构造函数的prototype,被称为隐式原型

1
console.log(new fun('yajue').__proto__ === fun.prototype)  // -> true

原型链

既然函数上的prototype是一个对象,那么它应该会有属于自己的__proto__

1
console.log(fun.prototype.__proto__ === Object.prototype)  // -> true

那么Object.prototype有没有自己的__proto__?答案是没有

1
2
console.log(Object.prototype)   // -> [Object: null prototype] {}
console.log(Object.prototype.__proto__) // -> null

Object.prototype已经是原型链的顶层了,如果这里不设计为null,那么将会无限地溯源上去,这不现实

一整个原型链大概是这样:

1
2
3
4
5
6
7
8
// 如果这个对象是fun构造函数的一个实例
{
__proto__: fun.prototype = {
__proto__: Object.prototype = {
__proto__: null
}
}
}

查找规则

先从自身找属性,如果找到则停止;如果没找到则往原型上找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function fun (name) {
this.name = name
this.a = 1
}
// ------
const f = new fun('Cheriko')
console.log(f.a) // -> 1
// ------
fun.prototype.b = 2
console.log(f.b) // -> 2
console.log(f.__proto__.b) // -> 2
// ------
fun.prototype.__proto__.c = 3
console.log(f.c) // -> 3
console.log(f.__proto__.c) // -> 3
console.log(f.__proto__.__proto__.c) // -> 3
// ------
Object.prototype.d = 4 // 和上一块差不多
console.log(f.d) // -> 4
console.log(f.__proto__.d) // -> 4
console.log(f.__proto__.__proto__.d) // -> 4
// ------
Object.prototype.__proto__ = 5 // 原型链顶端不能被修改
console.log(Object.prototype.__proto__) // -> null
console.log(f.__proto__.__proto__.__proto__) // -> null
console.log(f.e) // -> undefined

/** 最后的结构是:
* obj {
* a: 1
* __proto__: fun.prototype = {
* b: 2
* __proto__: Object.prototype = {
* c: 3
* d: 4
* __proto__: null
* }
* }
* }
*/