10.2 JS中的原型--难点分析以及原型链图解绘制

start

其实上一篇文章就基本说明了原型之间的关系。

这里再总结一下:

  • 函数的显示原型属性 prototype 是一个对象,也就是原型。
  • 对象的隐式原型属性 __proto__ 指向原型, __proto__ 将对象和原型对象连接起来组成了原型链。
  • 原型有一个属性 constructor 指向它对应的构造函数。

因为在 JS 中是没有类的概念的,为了实现类似继承的方式,通过 _proto_ 将对象和原型联系起来组成原型链,得以让对象可以访问到不属于自己的属性(即可以访问到原型链上的属性)。

特殊情况

1. 函数Tomato 的隐式原型和显示原型

其实上面的基本只是理解透彻了,基本原型这个东西就很简单了。但是有一些特殊情况需要研究说明一下。

我们知道,函数上有一个 prototype 属性,指向了它的原型对象。实例对象上有一个__proto__ 指向它构造函数的原型对象。

但是不要忘记了,函数也是一种特殊的对象!所以函数也会有__proto__属性。我们去看看他指向的什么

首先定义一个函数,我们打印一下它。

function Tomato() {

}

// console.dir 用于打印对象的详细信息
console.dir(Tomato)

image.png

根据上面的打印,我们可以得到这么一个关系图:

图一

image.png

函数 Tomato 的隐式原型和显示原型都在上面的图了。它的隐式原型,也是一个对象。这个对象的constructor指向的是Funciton。

2. 函数 Function 的隐式原型和显示原型

我们继续去打印一下构造函数 Function 的两个原型属性。

console.dir(Function.__proto__)
console.dir(Function.prototype)
console.dir(Function.prototype === Function.__proto__)

image.png

什么情况?Function的隐式原型和显示原型居然相等!!!

之前是 实例对象的隐式原型 指向 构造函数的显示原型 t1.__proto__ === Tomato.prototype

这里我看了很多的博客,原文结论如下:

Function作为一个内置对象,是运行前就已经存在的东西,所以根本就不会根据自己生成自己,所以就没有什么鸡生蛋蛋生鸡,就是鸡生蛋。

至于为什么Function.__proto__ === Function.prototype,我认为有两种可能:一是为了保持与其他函数一致,二是就是表明一种关系而已。

简单的说,我认为:就是先有的Function,然后实现上把原型指向了Function.prototype,但是我们不能倒过来推测因为Function.proto === Function.prototype,所以Function调用了自己生成了自己。

我的想法

  1. 除了Function,所有的函数都是new Function形成的。
  2. 其次,不用太过于纠结于这个地方,可以参考上面博客的理解思路。

得到最新的关系图

image.png

3.沿着原型链继续向上寻找

 function Tomato() {

}

// 函数Tomato的隐式原型的隐式原型,
console.log(Tomato.__proto__.__proto__)

// 函数Tomato的显式原型的隐式原型
console.log(Tomato.__proto__.__proto__)

console.log(Tomato.__proto__.__proto__.__proto__) // null
console.log(Tomato.__proto__.__proto__===Tomato.prototype.__proto__) // true

由上面的代码可以得到这么一个关系图。

image.png

这样一直找下去,发现:

  1. 它们的原型都指向了一个constructor为Obejct的对象。

  2. 原型的尽头,是null。

4. 看下Object

console.dir(Object.prototype)
console.dir(Object.__proto__)

console.log(Object.__proto__ === Object.prototype) // false

image.png

由上图可以得知这么一个关系。

image.png

5.总结原型链

  1. 我画了一个我理解的原型图解

image.png

  1. 原型链的本质就是为了实现属性的共享,理解原型了之后,你会发现,怪不得数组可以.push,字符串却不行。

  2. 还有一个我第一次接触原型的时候的痛点,prototype和constructor互相指来指去,一个圆环有什么用???其实,到我现在的认知里面,看图解,先只看 隐式原型__proto__即可。可以理解为 原型链通过__proto__链接起来的,就很好理解啦。

说个丢人的场景:不懂原型的时候,查看 prototype和constructor 查看了很久,哈哈哈…

image.png

  1. __proto__ ,绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Tomato.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.__proto__时,可以理解成返回了 Object.getPrototypeOf(obj)。 (我记得ES6之后开始官方支持了这个属性,但是我没找到官方的原本出处,就不说百分百确认的话了。)

其他优质图解

我虽然画了一个原型之间的关系图,但是还是有点草率,这里记录一下其他图解。其实懂了原型链,这个图看起来就很简单了。

image.png

本文参考的原型相关的优质博客。

END

原型学到这里基本ok啦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lazy_tomato

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值