【前端基础】盘根错节的寄生世界之原型和原型链
前言
原型链虽然概念上比较简单,用了奇怪的姿势模拟了继承的功能性,但是在整个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