• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

一篇文章让你搞懂原型和原型链

开发技术 开发技术 1周前 (11-20) 9次浏览

本文由葡萄城技术团队原创并首发

转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。

 

与多数面向对象的开发语言有所不同,虽然JavaScript没有引入类似类的概念(ES6已经引入了class语法糖),但它仍然能够大量的使用对象,那么如何将所有对象联系起来就成了问题。于是就有了本文中我们要讲到的原型和原型链的概念。

原型和原型链作为深入学习JavaScript最重要的概念之一,如果掌握它了后,弄清楚例如:JavaScript的继承,new关键字的原来、封装及优化等概念将变得不在话下,那么下面我们开始关于原型和原型链的介绍。

什么是原型?

JS中的对象包含了一个prototype的内部属性,这个属性所对应的就是该对象的原型。

我们先看下图:a、b、c 分别为数组、对象、函数。

一篇文章让你搞懂原型和原型链

 可以看到,三者都有一个属性:__proto__

一篇文章让你搞懂原型和原型链

 

 

这个 __proto__ 称作 隐式原型。

除此之外,c还有一个属性:prototype

一篇文章让你搞懂原型和原型链

 

这个prototype 称作 显式原型。

小结一下:

  • 所有引用类型(函数,数组,对象)都拥有__proto__属性(隐式原型)
  • 所有函数除了有_proto_属性之外还拥有prototype属性(显式原型)
  • 原型对象:每创建一个函数,该函数会自动带有一个prototype属性,该属性是一个指针,指向了一个对象,我们称之为原型对象。

函数除了有_proto_属性之外还拥有prototype属性,我们借助构造函数来寻找二者之间的关系。如下图:

一篇文章让你搞懂原型和原型链

 

 一篇文章让你搞懂原型和原型链

 

总结如下(结合上图更容易理解):

1. 实例对象a只有__proto__(隐式原型),构造函数既有 __proto__(隐式原型)也有prototype(显式原型)

2. __proto__ 和 prototype 都是一个对象,既然是对象,就表示他们也有一个 __proto__

a.__proto__.__proto__ 
A.prototype.__proto__

3.实例对象a的隐式原型指向它构造函数的显式原型,指向的意思是恒等于  

a.__proto__ === A.prototype

4. 当调用某种方法或查找某种属性时,首先会在自身调用和查找,如果自身并没有该属性或方法,则会去它的__proto__属性中调用查找,也就是它构造函数的prototype中调用查找

什么是原型链?

先看下图,提出一个问题:

1. 在 Object的显式原型添加属性b,为什么 示例对象p 能使用此属性呢? p.b = b

2. 为什么 p.a 为undefined

一篇文章让你搞懂原型和原型链

 

 

如下图所示

1.    实例对象p的隐式原型(__proto__)是一个对象,有两个属性值:constructor 和 __proto__

2.   p.__proto__.constructor 返回的结果为构造函数Person

3.   p.__proto__.__proto__ .constructor 返回的结果为Object()函数

一篇文章让你搞懂原型和原型链

 

 

结合上面所讲的显式原型与隐式原型之间的关系,等同如下:

1.  p.__proto__.__proto__  =  Object.prototype

所以p.b打印结果为b,p没有b属性,会一直通过__proto__向上查找,最后当查找到Object.prototype时找到,最后打印出b,向上查找过程中,得到的是Object.prototype,而不是Function.prototype,找不到a属性,所以结果为undefined,这就是 原型链,通过__proto__向上进行查找,最终到null结束。

总结:

1.   查找属性,如果本身没有,则会去__proto__中查找,也就是构造函数的显式原型中查找,如果构造函数的显式原型中也没有该属性,因为构造函数的显式原型也是对象,也有__proto__,那么会去它的显式原型中查找,一直到null,如果没有则返回undefined

2.   p.__proto__.constructor  == function Person(){}

3.   p.___proto__.__proto__== Object.prototype

4.   p.___proto__.__proto__.__proto__== Object.prototype.__proto__ == null        

5.    通过__proto__形成原型链而非protrotype

一篇文章让你搞懂原型和原型链

 

什么是原型继承?

Person.prototype 只是一个指针,指向的是原型对象,但是这个原型对象并不特别,它也只是一个普通对象。假设说,这时候,我们让 Person.prototype 不再指向最初的原型对象,而是另一个类 (Animal)的实例,情况会怎样呢?

 一篇文章让你搞懂原型和原型链

 

执行该代码   Person.prototype = new Animal() 后,Person的prototype指针指向发生了变化,指向了一个 Animal 实例。

 

当 p 去访问 address 属性时,js会先在 p 的实例属性中查找,发现找不到后,就会去 Person 的原型对象上 查找。因为Person的原型对象已经被我们换成一个animal实例,所以就会先找animal实例的属性,当发现还是没有 address属性,就会去Animal的原型对象上查找,最终找到。

这就说明,我们可以通过原型链的方式,实现 Person 继承 Animal 的所有属性和方法。

结语

看到这,相信大家对原型和原型链的概念应该已经有了一定了解了,如果仍然不太理解,也不用气馁,因为闭包及原型链是JavaScript最难理解的几部分。相信之后在不断的开发实践中会使你理解的更为透彻,多学习多思考才能更快掌握。如果大家有任何反馈和问题,也欢迎通过评论区告诉我,谢谢。

 


喜欢 (0)