frankfan的胡思乱想

学海无涯,回头是岸

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对象

image.png

可见,无论是info中的字段__proto__还是Info中的字段prototype,都是指向同一块内存区域中的所谓「原型」对象,既然这个「原型」也是一个对象,那必然也拥有一个叫__proto__的字段:

image.png

显然,这个内存中的「原型」对象的字段__proto__指向一个叫Object对象的prototype字段并最终一起指向另一个内存中的「原型」对象。

既然Object这个对象拥有字段prototype,显然,它是个函数。

事情逐渐奇怪起来,既然图示中「绿色」所示的也是一个原型「对象」,那么这个对象的__proto__字段指向何方呢?答案是:为了避免永无休止的指向,此时,这个「绿色」对象的__proto__被设置为了null(呵呵逻辑自洽哪有什么秘密可言,总在哪出动了点小手脚而已)。

所以至此,原型链在null处终结,一切真相都在眼前。


这里还有2个小问题需要解释下:

  • info对象是由Info函数构造(new)得来,那么infoInfo之间的关系通过什么维护
  • 这种原型链的设计方式意在何为,有没有别的实现方式

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本身不提供其他方法,但是因为其组合了obj1obj2两个对象,那么变相的等价其同时拥有了obj1obj2的属性和方法。

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编辑  收藏  举报

导航