原型链之前一直都不是很理解,这两天把《你不知道的JavaScript》和《JavaScript高级程序设计》的原型链那章看完后有所理解,在这里先记下来,加深印象。
什么是原型对象要讲清楚什么是原型链需要从原型对象开始谈,那么什么是原型对象呢?《JavaScript高级程序设计》中是这样讲的:
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。
简单来说,原型对象也是对象,但是通过原型对象可以实现对象的属性继承。
这里用《JavaScript高级程序设计》这本书上的demo来解释一下:
function Person () { } Person.prototype.name = "Nicholas" Person.prototype.age = 29 Person.prototype.job = "Software Engineer" Person.prototype.sayName = function () { console.log(this.name) } var person1 = new Person() var person2 = new Person()
这里声明了一个Person函数,没有定义任何属性;但是在Person的原型对象里定义了name,age,job属性和sayName方法。之后创建了两个Person的实例对象,person1和person2。在这里构造函数,原型对象,实例对象三者的关系用一张图片表示就是
(图片来源谷歌,侵删)
如图所示,Person的prototype指针指向它的原型对象,实例对象的[[Prototype]]指针也指向它的原型对象。这里简单说明一下什么是[[Prototype]]指针:
调用构造函数创建一个新实例之后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象,这就是[[Prototype]]指针。
现在,person1和person2的[[Prototype]]都指向了Person.prototype,这样的话Person.prototype里的方法和属性是被person1和person2共用的。
如何证明他们共用Person.prototype里面的属性和方法?请执行下面的语句:
person1.sayName() // "Nicholas" person2.sayName() // "Nicholas"
有人说了,你这初始化的name属性只有一个,执行的结果当然都一样啊。那么请再试试下面这句:
console.log(person1.sayName === person2.sayName) //true
结果很明显,person1.sayName和person2.sayName指向的是同一个方法。到这里可能有人会疑惑:一开始的代码中Person函数里并没有定义任何属性和方法,为什么person1和person2能执行sayName方法?那么这就要来谈谈对象属性调用的过程了。
以上面代码为例,当你执行person1.name时,解析器会开始查找person1中有没有name属性,如果找到了则返回属性值;如果没找到,则在person1的原型对象中继续找,找到了则返回属性值;如果还没找到,就沿着原型链往上继续找,如果最终还是没找到就返回undefined。
到这里大家也明白了多个对象实例共享原型对象的属性和方法的基本原理了,那么有人又会问了,如果我通过给实例对象属性赋值能不能重写原型对象里的属性和方法呢?
答案是不行的,在实例对象中对原型对象中的同名属性赋值会屏蔽原型对象中的属性。简单解释就是,对person1中的name属性赋值会直接在person1中添加name属性。但是有两种情况下,当name属性不存在于person1中而存在于原型对象中时,直接给person1.name赋值会有不一样事情发生:
1.当原型对象中的name属性标记为只读(writable: false)时,对name属性的赋值不会在person1添加name属性,也不会修改原型对象中的name属性,在严格模式下还会报错。
2.当原型对象中的name属性是一个setter,那么对person1中的name属性执行赋值语句就会调用setter,但name不会被添加到person1中。
这部分如果不懂什么是只读和setter,大家可以去看一下Object.defineProperty。
其实讲到这里,原型对象是什么已经基本清楚了。那么原型链就很简单了,继续上面的demo:
function Parent () { this.parentName = "noOne" } function Person () { this.name = "Nicholas" } Person.prototype = new Parent() var person1 = new Person()
这个例子中,我们把Parent的实例对象赋给了Person的原型对象。Person.prototype中的[[Prototype]]此时指向了Parent.prototype。举一反三,Parent.prototype也可以是另一个原型的实例对象,这样不断地层层递进便构成了原型链。
原型链的主要作用便是实现继承,这部分后续的文章我会继续讲。
本人经验尚浅,目前对于前端仍在不断摸索和学习,文章如有错误,欢迎各位指正。最后附上本人博客地址和原文链接,希望能向各位多多学习。
lbj的前端之路
原文链接:学习JavaScript之原型链
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/91722.html
摘要:我们用一张图表示构造函数和实例原型之间的关系好了构造函数和实例原型之间的关系我们已经梳理清楚了,那我们怎么表示实例与实例原型,也就是或者和之间的关系呢。 开篇: 在Brendan Eich大神为JavaScript设计面向对象系统的时候,借鉴了Self 和Smalltalk这两门基于原型的语言,之所以选择基于原型的面向对象系统,并不是因为时间匆忙,它设计起来相对简单,而是因为从一开始B...
摘要:我们用一张图表示构造函数和实例原型之间的关系好了构造函数和实例原型之间的关系我们已经梳理清楚了,那我们怎么表示实例与实例原型,也就是或者和之间的关系呢。 开篇: 在Brendan Eich大神为JavaScript设计面向对象系统的时候,借鉴了Self 和Smalltalk这两门基于原型的语言,之所以选择基于原型的面向对象系统,并不是因为时间匆忙,它设计起来相对简单,而是因为从一开始B...
摘要:实现原型链的方式如下让原型对象称为另一个构造函数的实例这个实例继承了的属性上述代码继承是通过来实现,创建的实例,并将该实例赋给。无疑,集两者之大成,这才是最常用的继承模式。 原型链 JavaScript的继承主要依靠原型链来实现的。我们知道,构造函数,原型,和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个原型对象的指针。 实现原型链...
摘要:除此之外,原型是共享的,如果我们有的写法,改变这两个对象任何一个的原型都会影响另外一个,这在大多的情况下是不可取的。当对象查找一个属性的时候,他会沿着原型链一直往上追踪,直到直到为之。在性能方面,原则上应该尽量避免原型链太长。 简介 如果之间学习过cpp 、java 之类的语言,都会知道他们是可以基于类 class 进行继承的, 在JavaScript 中,并没有类继承这个概念,要实...
摘要:深入系列的第一篇,从原型与原型链开始讲起,如果你想知道构造函数的实例的原型,原型的原型,原型的原型的原型是什么,就来看看这篇文章吧。让我们用一张图表示构造函数和实例原型之间的关系在这张图中我们用表示实例原型。 JavaScript深入系列的第一篇,从原型与原型链开始讲起,如果你想知道构造函数的实例的原型,原型的原型,原型的原型的原型是什么,就来看看这篇文章吧。 构造函数创建对象 我们先...
阅读 2980·2021-10-27 14:15
阅读 2956·2021-09-07 10:18
阅读 1297·2019-08-30 15:53
阅读 1547·2019-08-26 18:18
阅读 3356·2019-08-26 12:15
阅读 3434·2019-08-26 10:43
阅读 613·2019-08-23 16:43
阅读 2173·2019-08-23 15:27