原型链以前一直都不是很理解,这两天把《你不知道的JavaScript》和《JavaScript高级程序设计》的原型链那章看完后有所理解,在这里先记下来,加深印象。前端
要讲清楚什么是原型链须要从原型对象开始谈,那么什么是原型对象呢?《JavaScript高级程序设计》中是这样讲的:函数
不管何时,只要建立了一个新函数,就会根据一组特定的规则为该函数建立一个prototype属性,这个属性指向函数的原型对象。学习
简单来讲,原型对象也是对象,可是经过原型对象能够实现对象的属性继承。
这里用《JavaScript高级程序设计》这本书上的demo来解释一下:this
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。在这里构造函数,原型对象,实例对象三者的关系用一张图片表示就是
(图片来源谷歌,侵删)spa
如图所示,Person的prototype指针指向它的原型对象,实例对象的[[Prototype]]指针也指向它的原型对象。这里简单说明一下什么是[[Prototype]]指针:prototype
调用构造函数建立一个新实例以后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象,这就是[[Prototype]]指针。设计
如今,person1和person2的[[Prototype]]都指向了Person.prototype,这样的话Person.prototype里的方法和属性是被person1和person2共用的。指针
如何证实他们共用Person.prototype里面的属性和方法?请执行下面的语句:code
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也能够是另外一个原型的实例对象,这样不断地层层递进便构成了原型链。
原型链的主要做用即是实现继承,这部分后续的文章我会继续讲。
本人经验尚浅,目前对于前端仍在不断摸索和学习,文章若有错误,欢迎各位指正。最后附上本人博客地址和原文连接,但愿能向各位多多学习。