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 阅读(39) 评论(0) 收藏 举报
浙公网安备 33010602011771号