likes
comments
collection
share

【前端基础】盘根错节的寄生世界之原型和原型链

作者站长头像
站长
· 阅读数 37

前言

原型链虽然概念上比较简单,用了奇怪的姿势模拟了继承的功能性,但是在整个Javascript的世界里面,它的的关系错综复杂,很容易被它们之间的关系绕进去

  • __ proto __[[prototype]]prototype傻傻分不清楚,
  • Object创建了Function,还是Function创建了Object

所以我想要从头梳理一遍,让大家(自己)更清晰的看清原型链。

开始之前

回顾我们学习Javascript的时候,一定听说过一句话

一切皆对象

这句话说的非常准确,但是有一点例外,就是primitive基础类型值,不过在使用其方法的时候也会自动被包装成对象,不过这是题外之话。

这里你肯定有一个疑问,那么我们执行的function是对象吗?

答案是肯定的,以下我会将函数称为函数对象

说完这个,我们正式开始吧

正文

最原始的原型结构

【前端基础】盘根错节的寄生世界之原型和原型链

const obj = {};

在Javscript中如果我们这样声明一个对象obj,那么它的原型结构如上图。

Javascript它通过Object()构造函数创建了obj,并且创建的obj都有一个隐藏内置属性[[prototype]]并把它指向Object.prototype属性,Object.prototype的constructor指回构造函数Object(),而Object.prototype之中也有一个[[prototype]],它指向了虚无null

而这个Object.prototype是所有原型链(prototype chain)的终点

记住这个结构,这个结构可以推导出所有的原型链,它是Javascript世界的基本结构!

逐渐复杂的原型

我们除了上面说的第一种创建对象的方式还有另外一种,就是通过构造函数的方式来创建

function Bar() {}
const foo = new Bar(); 

类比上面结构,我们得出以下结构

【前端基础】盘根错节的寄生世界之原型和原型链

上面提到,Object.prototype是所有原型链的终点,而Bar.prototype就是一个普通的对象,那么这个?就是Object.prototype, 结合第一张图,得到以下的图

【前端基础】盘根错节的寄生世界之原型和原型链

到此我们创建最基础的原型链(prototype chain),所有的原型链(prototype chain)都是基于这个结构不断累加,不过现在只说清楚了普通的对象,Javascript的世界一切皆对象,函数对象又是谁创建的?不过在说这个之前我们先把一些概念说清楚。

__ proto __[[prototype]]prototype三姐妹

现在prototype[[prototype]]已经浮出水面,加上__proto__就来说说它们之间的区别

[[prototype]]它是每个Javascript对象都会有的隐藏内置属性,为的都是能让每个对象都能顺着它的prototype chain往上找,也就是每个对象与生俱来的能力,它的终点是null

__proto__ 是一个非正规的访问[[prototype]]属性的方式,本质上是一个定义在Object.prototype上面的属性__proto__,通过getter,setter方法操作访问。obj本身没有__proto__ 没有这个属性,但是通过[[prototype]]我们访问到了Object.prototype上面的__proto__(通过this绑定,我们知道了是obj的),所以获取到了[[prototype]]的对象。 不过现在并不推荐这么做,而是推荐使用Object.getPrototypeOf(obj)Object.setPrototypeOf(obj, parent)来访问操作__proto__。

prototype它只存在在函数对象上面的一个正常公开的属性,Bar.hasOwnProperty('prototype')这个可以证明, 不过函数对象自然也有一个[[prototype]],它指向哪?我们先按下不表。

先通过代码验证一下,刚刚说过的结构

const obj = {};
function Bar() {}
const foo = new Bar(); 


obj.__proto__.__proto__ // null
obj.__proto__ === Object.prototype /// true
bar.prototype.__proto__ === obj.__proto__ //true
bar.prototype.__proto__.__proto__ // null

obj.__proto__ === Object.getPrototypeOf(obj) // true
Object.getPrototypeOf(obj) === Bar.prototype // false
Object.getPrototypeOf(obj) === Bar.prototype.__proto__ // true

foo.__proto__ === Bar.prototype // true
foo.prototype // undefined
Bar.prototype // {constructor: ƒ}

函数对象从哪里来

Javascript世界所有对象都源自两个创世神,Object和Function,一个创建普通对象,一个创建函数对象,而Object本身就是一个构造函数,所以它也是被Function所创建。

通过前面的基础结构,我们可以简单推断出一个函数对象它的原型结构是什么样子

【前端基础】盘根错节的寄生世界之原型和原型链

注意[[prototype]]并不是prototype这个公开的属性,它就是__proto__,前面已经详细解释过三个东西

function Bar(){}
Bar.__proto__ === Function.prototype // true

通过代码验证了,结构确实是这个结构,我们继续往上探索,Function.prototype的[[prototype]]是什么?Object创建普通对象,Function创建函数对象,Function.prototype是一个普通对象,自然是Object创建,?是什么呼之欲出,我们先用代码推断一下。

Function.prototype.__proto__ === Object.prototype // true
Object.__proto__ === Function.prototype // true

通过以上两行代码,我们终于把Function和Object联系起来了,也证明了Object确实是Function创建。

【前端基础】盘根错节的寄生世界之原型和原型链

看到这里感觉是不是少了点什么?Function它也是一个函数对象,它的[[prototype]]呢?

根据上面的结构推断,只有可能是Function自己创建了自己,所以它的[[prototype]]指向了Function.prototype。

这里没有鸡生蛋,蛋生鸡的问题,这里的自己很明显是由native code创建了,也就是我们的JS引擎里面的原生代码,然后Function才执行自己的本身的功能。

到此,我们完成了Javacript世界之中原型链prototype chain的关系梳理。

下面我会放出一张非常经典流传十分之久的关系图,想不通的时候不妨看看最基本的那个结构,通过基本结构类比函数对象的结构,再把它们结合起来,就能变成下面这张图。

【前端基础】盘根错节的寄生世界之原型和原型链

一些奇怪的代码

在Javascript之中我们有一个a instanceof b操作符,它的作用是判断b是否在a的原型链上面,它的原理就是

a.__ prot o__. ..... .__ proto __ .constructor === b

先小试牛刀

function Bar() {}
const Foo = new bar()
foo instanceof bar // true

结合上面所分析,再来看看以下代码,一切都说的通了

Object instanceof Function // ture
Object.__proto__.constructor === Function
Object instanceof Object // ture
Object.__proto__.__proto__.constructor === Object
Function instanceof Function // ture
Function.__proto__.constructor === Function
Function instanceof Object// ture
Function.__proto__.__proto__.constructor === Object
转载自:https://juejin.cn/post/7203268889462243365
评论
请登录