JavaScript中原型链的个人浅薄理解

以下内容都是基于读者有了一定的原型链理解基础上写的,仅仅只是浅薄的理解,如有错误欢迎指出。

首先需要明白这么一点,也是理解原型链的基础:原型链的作用是什么?
为了回答这个问题,要引入以下情景(或许你看过,但这个例子我认为是最合适作为理解原型链的作用的一个):

function Cat(name){  // 定义一个动物的构造函数
    this.name = name;
    this.voice = function () {
    	console.log('喵喵');
  };
}

let cat1 = new Cat('mimi');  // 
let cat2 = new Cat('kiki'); 

以上代码中,我通过Cat构造函数,定义了两个对象:cat1cat2,他们都有对应的名字和方法,但是存在一个问题,cat1和cat2中都会存在一个 voice 方法,直观上看他们是一样的,但是实际上这俩方法并不相等:

console.log(cat1.voice === cat2.voice);  // 结果为false

这表示通过构造函数生成对象的时候,内部的变量和方法都会重新生成,且需要重新开辟内存空间,这就导致了性能上和空间上的浪费:因为对于每一个猫猫来讲,他们都会发出叫声,所以这个方法应该是Cat 构造函数生成的 cat 对象之间共享的。
所以为了解决这个性能、空间上会浪费的问题,引入了原型链的概念。


在讲我个人的理解之前,首先要讲下上面那个理解如何让voice在对象之间共享:

Cat.prototype.voice = function () {
    console.log('喵喵');
};

cat1.voice();  // 发出喵叫
cat2.voice();
console.log(cat1.voice === cat2.voice); // true

直接在 Cat 构造函数的 prototype 对象上定义voice方法即可。
这么直接写一段抽象的话可能很难理解,但是不要害怕,往下看。

我以我个人的理解来阐述下我是如何去理解原型链这个概念的。

我默认大家对 __proto__、prototype等属性已经有了一个大致或者全面的了解。不了解的话可以先去了解下,知道他们是干嘛,有啥关系就行。

原型链的理解,我着重在字,想象一串链条,一条一条链条结组成长长的链,可以从末端顺着链条爬上顶端,有开始也有结束,他们之间通过链条结之间一个一个互相连接起来
链条-百度百科
每一个由构造函数生成的对象都会有一个__proto__属性,该属性只存在对象中,而在构造函数中含有prototype属性。比如上面的例子中,cat1就有:cat1.__proto__,而Cat有:Cat.prototype
二者是严格相等的!

console.log(cat1.__proto__ === Cat.prototype);  // true

于是就可以这么说:cat1的原型对象是Cat。可能有的小伙伴对原型对象中这个原型二字感到很抽象,很陌生,或者说很难理解,那我换一个翻译:雏形。
在这里插入图片描述

雏形是啥?CPU的原材料是沙子,或者说雏形是一堆沙子;借用西游记里孙悟空的一句话:妖怪,我要把你打回原形!比如是对一个由蛇精化作的美女而言,孙悟空一棒下去,直接把美女打成一条蛇了,为啥?因为本质没改变,外表是美女,本质是一条蛇,她的雏形(原型)就是一条蛇。

回到上面那个例子,cat1 的原型(雏形)是啥?
那肯定就是 Cat 了,因为 cat1 是由 Cat “变”(用变字可能不是很准确,但你能理解我的意思,对吧?)来的,cat1 再怎么改变,都无法脱离它的本质就是 Cat,换句话说,原型就是 Cat

理解了上述内容,你可能就有疑问了,别的博客内写的:通过原型链(到现在为止,你都可以在大脑内想象一条链条存在,想象不出来看我上面贴的图),可以让对象访问到原型对象上的各种方法,甚至原型对象的原型对象上的方法(是不是比较绕?)。
其实很好理解:一条蛇化作了人,蛇有七情六欲,蛇会吃喝拉撒(神话意义上的蛇,不是动物世界的那种),那化作了人之后,不可能自己原来会做的事情,化成人了就不会做了吧?照样会吃,会动情,动怒。对应到Cat那个例子中去,就是:

Cat 会做的,cat1 照样会做。
Cat 在其 prototype 上定义的 voicecat1cat2就能直接使用,为啥?因为他俩的本质(雏形,原型)就是CatCat会的他俩自己也会。
是不是还是比较难理解?那我换个说法:
Cat一出生就会的东西,它化成了其他样子,难道就不会了嘛?(抽象意义上的出生)
婴儿刚出生时就会的呼吸,长大成人了就不会了嘛?
例子可能不是好例子,但是这么理解我觉得是可以的。

你到目前为止可能一直有个疑问:是怎么会的?cat1是怎么会知道Cat有 voice 这个方法的?等等一堆疑问,解决这些疑问的唯一关键字就是:__proto__
没错,对象和它的原型对象(雏形对象,本质对象)之间就是通过__proto__ 起来的!
注意这个字!

正如链条的链接需要通过一个一个链条结:在这里插入图片描述

对象和原型对象(雏形,最初的那个对象)之间也是需要这种类似“”的东西,来让它们之间链接起来,形成一个整体,一条完整的链条。这个“”就是:__proto__
就是这个小东西,让对象和原型对象之间有了连通的通道,有了从链条底端,向链条开端爬去的利器。通过它,对象就能获取到原型对象上定义的方法(也就是定义在 prototype 对象上的方法,实际上在前面,我通过一段代码展示了对象的 __proto__属性是严格等于构造函数上的 prototype,就可以将二者看成是同一个东西,但我怕太抽象,所以就通过简单的例子来讲述这点。)。
你可以想象成在底下链条底下有只蚂蚁向上爬,在链条中的某个结点上有一小撮蜂蜜,蚂蚁爬上第一个结点,发现没有,继续向上爬,直到找到那个蜂蜜。
这里Js解释器就是通过__proto__属性一个一个向上去寻找,在cat1对象本身内,没有定义voice,那么就通过__proto__,发现它与Cat相等,就在Cat内寻找,发现有voice这个方法,于是就调用了。


很明显,小伙伴肯定也会意识到一个问题,链条有底端,也有开端,那么在原型链概念中,是否也存在呢?

答案是显然的,有相关知识的小伙伴就会明白原型链的最顶端是null,正如链条的开端上面是空气一样。
借由前一个蛇精的例子,蛇是卵生,最开始就是一个卵。
所以有这么一个对应关系:人<----蛇精<-----蛇蛋。
蛋怎么来的?那就当做是空气中突然冒出的吧(null)。
前面的所有文字中,我刻意省略了Object这个东西,相信看过不少博客的小伙伴记都记住了这样一个关系(大致模样):obj <----> Funciont <----> Object <----> null
原型链的基础是从null开始,慢慢延伸出Object,再是Funcion,再是一个具体的对象(obj)。
它们之间怎么链接起来的?
答:通过 __proto__

好了,以上就是我思考原型链的全过程。
现在回过头去看看原型链,或者说想一想原型链,你有没有理解到?
问问自己原型(prototype)翻译成雏形会不会更好理解一点,原型链是为了解决什么问题而造出来的,对象和原型对象之间通过是什么链接起来,二者有啥对等关系没有?

可能我理解的,或者说我书写的有误,欢迎大家指出。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C01acat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值