前言
本文旨在通过图文的方式,一步步回顾原型链的整个流程是如何运作的,如果你刚好在电脑旁边,不妨跟着我的思路,一起走一遍敲一遍代码流程,你会发现原型链并没有你想的那么复杂。
new关键字
我们先看这一个代码,你要记住的第一点是:所有的对象都是通过new
这个关键字出来的
const a = new A();
这个时候可能会有人说,那我可以这样:
const b = {}
他实际上是一个语法糖:本质其实是:
const b = new Object()
那么此时我们可以得到一个关键点就是,对象a是由函数A通过new这个关键字生成的
,示例图如下所示:
原型
我们要记住的第二点是:每一个函数(A)它都有一个原型(箭头函数除外),也就是prototype
,它是一个{}
A.prototype ///是{}
而通过new这个关键字创建出来的对象(a),它也有一个原型__proto__
,它指向的就是函数的原型prototype
也就是:
a.__proto__ === A.prototype //true
至此,你可以成功的画出这个关系网,这就是初步的原型链:
推导
我们进一步拓展,我们需要思考一个问题,prototype是哪里来的呢?
我们通过打印可以知道A有一个prototype属性
,并且在控制台显示的是一个{}
,但是你注意,他并不是纯粹的空对象哦,你直接写{},实际上是新创建了一个空对象,那么肯定是不一样的。
错误的:
A.prototype ==={} //错误的❌
正确的:
A.prototype===Object.getPrototypeOf(a) //正确✔
那么至此,你可以画出这样的图:
你要明白一点,对象也是一个特殊的函数,那么他肯定也有prototype
属性,那么你就可以顺理成章的画出下面的这个关系:
A.prototype.__proto__===Object.prototype //正确✔
继续推导,那Object的prototype是哪里来的呢?如果Object的prototype也是Object,那就无限死循环下去了,所以Object.prototype是JS引擎直接安放在内存中的,它指向一个特殊值null
Object.prototype //[Object: null prototype] {}
那么Object是哪里来的呢,根据官方的说法,对象就是一个可以被调用的函数,我们上面说了,对象是通过new这个关键字得来的(这一点很重要),那Object实际上是由new Funtion()得来的
请你尝试下面这个代码:
typeof Object //function
function sum(a,b){
return a+b
}
const sum2 = new Function('a','b','return a+b')
sum(1,2) //3
sum2(1,2) //3
那么你又可以拓展这个图,函数也是一个特殊的对象,那么它肯定也有prototype
那么你会发现,(A)通过new这个关键词创造的东西(a),它(a)的隐式原型(proto)永远跟(A)的prototype相等,你如果发现这点规律,那么恭喜你,你可以直接得出这个图:
Function.prototype==Object.__proto__ //true
规律
我们总结一个规律A是父亲,这个父亲可以是一个对象,也可以是一个函数,因为函数本身就是一个可以被调用的特殊对象,所以你只需要记住一点,所有的对象都是通过new这个关键字创建的
你可以很轻易的发现,这里存在这些三角关系:
那么你可以进一步缩略这个图,得到最终的图:
那到底什么是原型链呢,你看看绿色的部分,是不是连成了一条线,这就是原型链,a找不到的属性会去(A)prototype找,(A)prototype找不到会去(Object)prototype找,如果还是找不到就是null了,就是真的没有了。
如果你再仔细拓展,你会发现,A实际上也是通过new Function创建出来的,这也就是解释了,为什么A身上有bind和call这些东西,也可以解释,为什么也有toString这个方法,因为Funcion也是Object。
那么Function从哪里来的呢?
这也是JS引擎直接放在内存中的。
所以Funcition和null都是JS引擎直接放到内存去的,相当于原始祖先了(女娲)。
道
道生一,一生万物,链条总归是有终点,跟人的生命一样,总会走到尽头,一切皆空,就是尽头
你可以把原型链想象成人的一生,总归有下一代,下下一代,你可以从你长辈继承知识(继承和共享属性和方法),我们都来自同一个祖先,不断的传递自己的知识,金钱,财富给下一代,然后走到尽头空空如也,但我们仍然会被记住(构建对象之间的关系),这就是人生的价值、也是原型链的价值。