JavaScript中简明扼要的原型链
网上看了一圈关于JavaScript
这门语言的所谓「原型链」概念,鱼龙混杂,一些示意图毫无章法略微混乱,这篇小文不讲这些,只阐明3个属性的概念。
JavaScript
中有一种数据类型叫「对象」(在此不赘述对象具体是什么,不是什么)JavaScript
中有一种数据类型叫「函数」,函数是一种特殊的「对象」(有多特殊?特殊在这个对象有个专有名词叫函数,这个对象还能被调用执行)- 所以显然,对象不一定是函数,函数一定是对象。
- 所有「对象」都有一个属性,叫「
__proto__
」 - 所有「函数」都有一个属性,叫「
prototype
」 - 因为函数也是一种对象,所以,所有函数都有一个属性,叫「
__proto__
」
综上:
function Info(){
console.log("info")
}
let info = new Info()
/*
这里有2个对象,更准确的说是1个对象1个函数。因为函数也是对象,所以这里有2个对象。
其中,
info.__proto__指向某个对象{}(不妨称这个对象为info.__proto__对象,额~似乎有点蠢...)
Info.prototype也指向某个对象{}(不妨称....)
并没有什么意外,info.__proto__与Info.prototype指向的是同一个对象:
即:info.__proto__ === Info.prototype -> {一片内存}
*/
根据上面代码演示,可知info.__proto__
指向Info.prototype
对象
可见,无论是info
中的字段__proto__
还是Info
中的字段prototype
,都是指向同一块内存区域中的所谓「原型」对象,既然这个「原型」也是一个对象,那必然也拥有一个叫__proto__
的字段:
显然,这个内存中的「原型」对象的字段__proto__
指向一个叫Object
对象的prototype
字段并最终一起指向另一个内存中的「原型」对象。
既然Object
这个对象拥有字段prototype
,显然,它是个函数。
事情逐渐奇怪起来,既然图示中「绿色」所示的也是一个原型「对象」,那么这个对象的__proto__
字段指向何方呢?答案是:为了避免永无休止的指向,此时,这个「绿色」对象的__proto__
被设置为了null
(呵呵逻辑自洽哪有什么秘密可言,总在哪出动了点小手脚而已)。
所以至此,原型链在null
处终结,一切真相都在眼前。
这里还有2个小问题需要解释下:
info
对象是由Info
函数构造(new
)得来,那么info
与Info
之间的关系通过什么维护- 这种原型链的设计方式意在何为,有没有别的实现方式
JavaScript
通过constructor
字段来关联对象与「对象构造器」之间的关系。这种形式在普通的面向对象语言中就是「对象」与「类」的关系,但是JavaScript
从本质上并不提供「面向对象」的实现,只是从接口协议上存在对「面向对象」的形式支持,做到了形似和神似。(所谓面向对象的「支持」不仅仅是指在语法形式上,内存结构上也需要支持,而JavaScript
对象的内存结构并非传统面向对象编程语言的内存布局)
在prototype
所指对象中的constructor
字段所表示的就是该对象的构造器函数名字,意为通过该构造器函数生成的对象。
function Item(){
}
const item01 = new Item()
console.log(item01.constructor.name)//打印Item
上面简单的几行代码有几个说明:
1.item01
对象「本身」并没有一个叫constructor
的字段,这个字段来源于item01.__proto__
所指向的Item.prototype
对象(此处,原型链的作用可见一斑了,且按下不表)
2.既然item01
这个对象由Item
构造而来,那Item
这个对象由什么构造而来呢?答案是「Function
」,显然,为了逻辑自洽这个Function
对象(函数)是内建的。
function Item(){
}
console.log(Item.constructor)//[Function: Function]
//同理,Item对象自身并没有constructor这个字段,该字段位于Item.prototype上
JavaScript
中的原型链本质是一种「组合」的设计模式。而非通过对象继承来实现代码复用。
let obj1 = {}
let obj2 = {}
let obj3 = {
obj1:obj1,
obj2:obj2
}
以上,就是典型的组合模式。
对象obj3
本身不提供其他方法,但是因为其组合了obj1
和obj2
两个对象,那么变相的等价其同时拥有了obj1
和obj2
的属性和方法。
JavaScript
中创建一个对象,其本身不一定存在toString()
方法,但是这个对象有一个__proto__
对象,这个对象指向的原型对象提供了toString()
方法,这样就变相等价这个对象拥有了toString()
方法。
在JavaScript
中,当说到一个对象继承了某个对象,从而拥有了某个属性和方法,本质是因为这个对象组合了某个对象,从而拥有了那个对象的属性和方法,而这个所谓的组合,就是通过其__proto__
而来的。
let obj = {}
obj.__proto__sayitem = function(){
console.log('hello item')
}
obj.sayitem()//输出hello item
显然,obj
对象本身并不存在sayitem
方法,通过__proto__
字段找到原型对象后,在原型对象身上找到了sayitem
方法,这样「看起来」obj
对象就拥有了sayitem
方法。而__proto__
就是JavaScript
对象中所谓「继承」的秘密所在。
posted on 2022-04-01 15:14 shadow_fan 阅读(30) 评论(0) 编辑 收藏 举报