[JavaScript]JavaScript中的原型和原型链

JavaScript中的原型和原型链

上一篇博文总结了JS中的数据类型以及所要注意的事项,这篇博文打算顺着数据类型来理解一下关于原型和原型链的问题。

学习JS中的原型和原型链,要想理解其中的关系和用法,得先搞清楚几个概念。

全局对象

MDN中,关于全局对象的定义如下:

全局的对象( global objects )或称标准内置对象,不要和 "全局对象(global object)" 混淆。这里说的全局的对象是说在全局作用域里的对象。

ECMAScript 规定全局对象叫做 global,但是浏览器把 window 作为全局对象。

window 的属性就是全局变量。

全局变量

全局变量划分为两类:

  • ECMAScript所规定的属性
    • global.parseInt
    • global.parseFloat
    • global.Number
    • global.String
    • global.Boolean
    • global.Object
  • 私有属性(即浏览器自己添加的)
    • window.alert
    • window.prompt
    • window.comfirm
    • window.console.log
    • window.console.dir
    • window.document
    • window.document.createElement
    • window.document.getElementById

所有 API 都可以在 MDN 里找到详细的资料。

现在我们以第一种全局变量为例,说一下全局函数

全局函数

先来看一下这段代码:


var s = "string"
var s2 = new String("string")

变量 s 和 s2 的不同之处在于:两者所对应的内存地址不同,除此之外,s 创建的一个基本类型的值,即只有一个字符串 "string";而 s2 创建的是一个对象,里面除了s2的字符串值"string"之外,还包含了很多可以使用的属性。

如图:

当我们分别对 s 和 s2 进行操作时:

s.charAt(0)     //"s"
s2.charAt(0)    //"s"
s.name = "here"
s.name      //undefined
s2.name = "there"
s2.name     //"there"

我们可以看到,在对待 s 时,我们可以像使用对象一样去使用基本类型数据,但是却不能对其属性有效赋值。

之所以能这样去使用基本类型,是因为JavaScript引擎内部在处理对某个基本类型 s进行形如s.sth的操作时,会在内部临时创建一个对应的包装类型(对数字类型来说就是Number类型)的临时对象,并把对基本类型的操作代理到对这个临时对象身上,使得对基本类型的属性访问看起来像对象一样。但是在操作完成后,临时对象就扔掉了,下次再访问时,会重新建立临时对象,当然对之前的临时对象的修改都不会有效了。

现在我们的 s 和 s2 都可以使用类似charAt()、charCodeAt()等等的函数,那么这些函数又保存在哪里呢?我们在对诸如s、s2等对象或是临时对象操作的时候是分别给他们赋予这些函数吗?

接下来我们可以引出理解原型链的关键一步:

__proto__

对应上一个问题,这些函数保存在哪里?

这些函数保存在一个公用属性组成的对象里(暂称为A),

然后让每一个对象的 proto 存储这个 A 的地址。

(__proto__由浏览器生成)

s2.__proto__

如图:

s2.__proto__代表的是String特有的属性,如果我们要调用的函数在A(即String公有属性)中没有,那还怎么办呢?

他就会上溯一层,来到s2.__proto__.__proto__(即所有对象的公用属性,暂称为B)中查看:

s2.__proto__.__proto__

如图:

好了,我们的流程已经渐渐清晰:

我们需要对象s2读取一个属性,我们先从s2的本体找,如果他没有被赋予这个属性,那么通过s2.__proto__所记录的地址,找到A对象,如果这个A对象里面也没有这个属性,那么我们通过A对象中__proto__所记录的地址,找到B对象,如果B对象是最后一层,那么再找下去就是null。

再来简化一下:

s2 -> s2.__proto__ -> A -> A.__proto__ -> B -> null

现在我们终于可以把原型和原型链带上了:

A 其实就是 String.prototype

B 其实就是 Object.prototype

原型链是:

s2 -> s2.__proto__ -> String.prototype -> s2.__proto__.__proto__ -> Object.prototype -> null

自此为止,我们对于原型与原型链的概念已经了解得差不多,每当我们声明一个数据类型(除了undefined和null),他已经存在一条原型链,这条链的起点在于我们刚声明的数据,终点在于null,其中Object.prototype是所有对象的公用属性,我们可以通过__proto__,来查看属性对象合集中有没有包含某个属性。

总结起来就是:

var 对象 = new 函数();
对象.__proto__ === 对象的构造函数.prototype

再加上一张我用思维导图总结出来的图,希望能帮到对原型、原型链还感到困惑的朋友。

猜你喜欢

转载自www.cnblogs.com/No-harm/p/9502720.html