JavaScript的迷惑原型链

本文最后更新于 2024年6月7日 下午

虽然面试可能凉了,但是还是收获了知识,我还觉得OK~

准备前端的面试绕不开的一个问题就是“对JavaScript的原型链的理解”,我自然也详细了解了一下这个问题,随着问题的深入,我发现虽然之前我了解过原型链的思想,但是很多细节扣的都不够深入,我只是了解了原型链的基本思想,最深处还有宝藏(坑)等待发掘(懵逼)。

ES5

先看一个ES5中对象继承的基本实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var a = function () {
this.name = "i am a";
}
a.prototype.sayHello = function () {
console.log(this.name);
}

var b = function () {
a.call(this);
this.age = "22";
}

b.prototype = Object.create(a.prototype);
b.prototype.constructor = b;

这样就相当于b继承于a对吧?我们验证一下:

1
2
3
4
5
var obj_a = new a();
var obj_b = new b();

console.log(obj_b instanceof a);
// true

这里提一下instanceof的原理:

1
a instanceof B

就是在对象a的原型链上检查是否有B.prototype

OK! 我们再深入一下,看一下原型链上的关系:

1
2
3
4
5
6
console.log(Object.getPrototypeOf(obj_b) instanceof a);
// true
console.log(Object.getPrototypeOf(Object.getPrototypeOf(obj_b)) instanceof a);
// false
console.log(Object.getPrototypeOf(Object.getPrototypeOf(obj_b)) === a.prototype);
// true

也就是下图所示:

但是仅仅这样就算完了吗?那和之前的理解没啥区别啊。我们再继续深入一下:

1
2
3
4
5
console.log(Object.getPrototypeOf(a.prototype) == Object.prototype)
// true

console.log(Object.getPrototypeOf(Object.prototype) === null);
// true

我们知道类a不继承于其他类了,那么a的原型对象作为一个对象,它一定是由Object函数生成的,那么它的原型对象就是Object.prototype

Object.prototype的原型对象的比较特殊是null

下面开始逐渐令人迷惑了:

我们知道a()b()甚至是Object()他们都是一个函数,那么函数也是一个对象,既然这样他们也是有原型的,也会拥有一条原型链,那么它的原型对象是是谁呢?

也许直接思考原型对象不太好得出结论,我们换一个角度,它的构造函数是谁?我们知道有这样一个函数:Fucntion()。可以用它生成任意的函数对象:

1
2
3
4
console.log(Object.getPrototypeOf(a).constructor===Function)
// true
console.log(Object.getPrototypeOf(a)===Function.prortotype)
// true

那既然a()作为一个函数它可以使上文的式子成立,那么Function本身作为一个函数的构造函数,它也是一个函数啊,那么上面的式子按理说也可以成立啊。

1
2
console.log(Object.getPrototypeOf(Function).constructor === Function)
// true

对于这个迷惑的操作,可以理解为Function可以构造任何函数,Function作为一个函数,当然可以自己构造自己。

既然上面的式子成立,自然下面的式子也可以成立:

1
2
console.log(Object.getPrototypeOf(Function) === Function.prototype)
// true

那么Object()作为一个函数,自然也可以使上文中提到的式子成立:

1
2
3
4
console.log(Object.getPrototypeOf(Object) === Function.prototype)
// true
console.log(Object.getPrototypeOf(Object).constructor === Function)
// true

同时Function.prototype作为一个对象,那么它的原型对象也就是Object的原型对象。

1
2
console.log(Object.getPrototypeOf(Function.prototype) ===Object.prototype)
//true

大功告成!

ES6

ES6中引入了class这一用法,它其实是一个语法糖,与ES5中的对象用法大同小异。唯一有些不同的地方在于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class a {
name = "a";
sayHello() {
console.log(this.name);
}
}
class b extends a {
age = "22";
}

var obj_a = new a();
var obj_b = new b();

console.log(Object.getPrototypeOf(b)===a);
// true
console.log(Object.getPrototypeOf(b.prototype)===a.prototype)
// true

也就是说这时,b()作为一个函数,它的原型对象是a()


JavaScript的迷惑原型链
https://siegelion.cn/2021/04/12/JavaScript的迷惑原型链/
作者
siegelion
发布于
2021年4月12日
许可协议