解析原型链
原型链是JavaScript这门语言设计的十分有意思的地方之一。在解析原型链之前,我们需要了解以下几个重要的概念。
构造函数
构造函数是一种比较特殊的函数,它通常被人为地约定为函数名的首字母需要大写,且必须通过new操作符来进行调用(与普通函数的本质上的区别),它的作用是用来创建特定类型的对象。在JavaScript中也一些原生的构造函数,如Object、Array、Function等等。
实例
每当构造函数被new运算符调用时,都会创建出一个新对象,这个过程被称为实例化。而这个对象就被称为实例。也就是说任何对象都是一个实例,但关键在于创建这个实例的构造函数是谁?以及它的原型是谁?
原型
构造函数内部有个特殊的属性prototype,这个属性指向了一个对象,没错它就是原型也称为原型对象。原型对象是一个十分特殊存在,每当构造函数实例化一个对象时,这个实例的[[Prototype]]会默认指向构造函数的prototype。实例对象可以通过自身的[[Porototyoe]]属性找到原型对象,而原型对象可以通过自身的construcor属性找到是哪个构造函数创建了这个实例。(注意许多浏览器会把属性[[Prototype]]替代为属性__proto__。)
构造函数、实例和原型值之间的关系
为了进一步弄清楚构造函数、实例和原型之间的关系,我们可以从下面这张图片开始
- 实例dog通过自身的[[Prototype]]属性找到了原型Dog.prototype
- 构造函数Dog通过自身的prototype属性找到了原型Dog.prototype
- 原型Dog.prototype则通过自身的constructor属性找到了构造函数Dog
- 实例dog通过constructor属性找到了构造函数Dog。
为实例dog通过constructor属性找到了构造函数Dog这个过程不用实线箭头呢?这里到底有什么细节呢,让我们再来看一段简单的代码和一张图片
- function Dog(name){
- this.name = name;
- }
- let dog = new Dog("cheems");
从代码结合图片来看,我们不难发现实例dog上并没有constructor这个属性,而原型对象才拥有这个属性,那么实例是如何获取到这个属性呢 ,这就涉及到了JavaScript中一种特殊的行为——委托,下面我们就来了解一下什么是委托。
委托
当我们尝试去获取对象的某个属性值,但该对象并没有这个属性时,那么JavaScript 会试着从原型对象中获取属性值。如果那个原型对象也没有该属性,那么再从它的原型中寻找,依次类推直到该过程最后到达终点Object.prototype,如果仍然没有找到就返回undefined。这个过程被称为委托。
现在你就明白了,实例dog正是通过委托这种方式找到了创建自己的构造函数。在明白这一点之后,原型链也就呼之欲出了。
原型链
如果在第一个对象上没有找到需要的属性或者方法引用,引擎就会继续在它的[[Prototype]]指向的对象上进行查找。同理如果后者中也没有找到需要的引用就会继续查找它的[[Prototype]],依次类推直到到达Object.prototype,这一系列对象的链接被称为原型链。
我们可以用一张图来表示下
由于构造函数也是对象,所以它同样具有构造函数和原型。构造函数的prototype是实例的原型,并非自身的原型。自定义的构造函数同样需要借助[[Prototype]]找到原型,进而找到创建自己的构造函数——即原生构造函数Function,但请注意原生的构造函数Function的[[Prototype]]是指向了Function.prototype。
- console.log(Function.__proto__ === Function.prototype); // true
所有的原型对象都可以沿着原型链一直寻找至到找到最后的原型对象Object.prototype,然后Object.prototype再往上寻找就是Null,用来表示此处没有对象。
小结
充分了解原型链是理解JavaScipt这门语言重要环节之一,而且对理解JavaScript继承的实现也会有莫大的帮助。如果你已经对原型链梳理的差不多了,下一篇文章我将带你走近JavaScript继承的世界。
最后
有关JavaScript的原型链的分享就到这里了,如果阅读完本篇文章后,你对JavaScript的原型链有了更深一步地理解和认识的话,欢迎给笔者点赞鼓励!你们的支持是我继续分享有关JavaScrit知识的动力,谢谢阅读完本篇文章的亲们
原文地址:https://juejin.cn/post/6949814782862032909