上章节我们学习了原型链相关知识,这节讲解几个原型的练习题进一步去加深对原型的理解。
练习题1
首先,从稍微简单的开始入手,看如下JS代码:
function Foo(){
}
Foo.prototype.a = 1 //对构造函数Foo添加原型属性
var f1 = new Foo() //创建Foo的实例对象f1
//修改Foo的prototype地址指向
Foo.prototype = {
a = 2,
b = 3
}
var f2 = new Foo() //更改Foo的prototype地址指向后创建Foo的实例对象f2
console.log(f1.a) //1
console.log(f1.b) //undefined
console.log(f2.a) //2
console.log(f2.b) //3
产生这样输出结果的原因可从如下内存图得出答案。
在 Foo.prototype.a = 1为构造函数Foo()添加原型属性a后创建的实例对象f1时内存图如下所示:
通过Foo.prototype = { a=2 , b=3 }后,只是修改了Foo的prototype的指向,原来指向的Object实例对象仍然被之前Foo所创建的实例对象f1的__proto__所引用,所以f1的隐式原型依旧指向之前的prototype属性;
在修改了Foo的显式原型指向后创建的实例对象f2的隐式原型__proto__将会指向新的显示原型所指向的位置,可由如下内存结构图看出:
所以由上图可知,实例对象f1的__proto__指向的为Foo之前的prototype,所以f1.a=1 , f1.b=undefined;而实例对象f2的__proto__指向的为修改后的Foo指向的prototype,即f2.a=2 , f2.b=3。
练习题2
这道题稍微有些复杂,涉及到上章节补充的原型链相关知识,这里我们先看如下JS代码:
function Foo(){
}
Object.prototype.a = function(){
console.log("a()")
}
Function.prototype.b = function(){
console.log("b()")
}
var f1 = new Foo() //Foo的实例化对象
f1.a() // a()
f1.b() // Uncaught TypeError: f1.b is not a function
F.a() // a()
F.b() // b()
由于涉及到上章节讲到的内容,我们将上章节的原型链的图继续展示如下:
由上图可知:
- Foo的实例对象 f1 在查找 a() 方法时,沿着隐式原型链的__proto__查找,在Object的原型对象上最终查找到 a() 方法。
- Foo的实例对象 f1 在查找 a() 方法时,沿着隐式原型链的__proto__查找,一直查找到Object的原型对象的__proto__,__proto__为null,返回Uncaught TypeError: f1.b is not a function。
- 函数对象F()作为Function()的实例对象拥有__proto__属性,且其隐式原型的__proto__值与Function()的显式原型的prototype值指向的地址相同,所以此时F作为实例对象查找 a() 方法时沿着其__proto__的值逐个查找,在Object的原型对象上最终查找到 a() 方法。
- 函数对象F()在查找 b() 方法时,继续沿着其__proto__的值逐个查找,最终在Function()的prototype指向的地方找到 b() 方法。
笔者建议:
在初期理解或者做原型相关的题目时,最好在纸上将内存结构图画出,这样易于分析,在后期熟练后便可在脑海中构建关系图。