原型:拿走爷爷口袋里的香蕉
“无中生有”的对象
疑问
你有没有想过这样一个问题:
当我们在浏览器中构造一个空函数并利用这个函数创建一个对象输出时:
看起来并无异常,浏览器正常的输出了一个空对象。
但是,当我们想再点开一下Father
确认其中的属性时,奇怪的事情发生了,浏览器竟无中生有出一个名为Prototype
的对象。
这个Prototype
,就是可以被称之为函数祖先的属性:原型。
继承:无处不在的原型?
再有一个问题,我们都知道,事物不可无中生有,属性不可凭空捏造,那么你有没有想过,当你大大方方的使用length
属性来得到一个数组的长度时,这个属性又是从何而来?
答案仍然和原型有关。

空数组之所以包含有length
在内的如此多的属性,根本原因是在JavaScript这门编程语言中存在一个Array
的构造函数,且在Array
的原型上就定义好了这些属性,数据类型在创建时原型上又继承了Array
,所以才让数据类型可以有这些方法进行访问。
同时,这也引出了关于原型的一个重要作用:继承,毕竟你可不会放着现成的length
不用而在函数中去构造这样一个属性吧。
原型的基本概念:
-
是函数上的一个属性,它定义了构造函数制造的对象的公共祖先。
-
构造函数new出来的对象会隐式继承到构造函数原型上的属性。
-
实例对象可以修改显示继承到的属性,但无法修改隐式继承到的属性(原型上的)。
隐式原型和显示原型
在 JavaScript 中,"隐式原型"是一个相对于“显式原型”的概念。在 JavaScript 中,每个函数都有一个显式原型属性(prototype),指向一个对象,这个对象被称为原型对象。而函数对象的原型被称为隐式原型,实际上是指向其构造函数的显式原型属性。
接上文,现在这个对象F
想吃香蕉了,我们在他的构造函数Father
的原型上添加一个eat
函数,那么F
能吃到香蕉吗?
答案是F
吃到了香蕉,真是可喜可贺,那么谁又能想到,F
吃香蕉的过程居然涉及到隐式原型和显示原型的查找呢?
别看图中对象F
的隐式原型和函数Father
的显示原型是分开画,但实际上它们就是一个东西。 只是角度不同导致称呼不同。
在这个过程中,对象F
先是查找自己属性,发现其中没有eat
这个属性,于是再到对象F
自己的隐式原型也就是继承来自函数Father
的显示原型中查找,成功找到eat
,于是输出“吃香蕉”。
总结一下就是:
-
对象的隐式原型 === 创造他的构造函数的显示原型。
-
js引擎在查找属性时,会先查找对象显示具有的属性,找不到再查找对象的隐式原型(
_proto_
)。
继承拓展:原型链
概念:js引擎在查找属性时,会顺着对象的隐式原型向上查找,查不到,则查找隐式原型的隐式原型,一直向上,直到找到null
为止,这种查找关系,称之为原型链。
就像是现在,儿子也想吃香蕉了,怎么办呢?没办法,那就找吧,好在原型上有这层关系,自己那没有就去Father
那找,Father
那没有就去GrandFather
那找。

这种自下而上儿子顺着爸爸找到爷爷口袋(原型)里的香蕉的查找关系,就被称为原型链。
但是你看,爷爷口袋里有香蕉,即使到了孙子这辈都能被他知道,这可不就是原型的魅力么。
转载自:https://juejin.cn/post/7375035008262455330