使用JavaScript闭包遇到的陷阱(一)
陷阱:在类的原型对象中添加特权方法
首先定义一个Page类,该类中有一个私有变量dom:
function Page(){
var dom;
}
定义2个特权方法来访问、修改私有变量dom:
function Page(){
var dom;
this.setDom=function(newDom){
dom=newDom;
};
this.getDom=function(){
return dom;
}
}
然后我们对Page类进行测试:
var page1=new Page();
page1.setDom("<div>page1<div>");
console.log(page1.getDom());//<div>page1<div>
var page2=new Page();
page2.setDom("<div>page2</div>");
console.log(page2.getDom());//<div>page2</div>
console.log(page1.getDom());//<div>page1<div>
到目前为止,Page类正常工作。
这时问题来了:我想每个Page类的对象都有相同的特权方法,那就将这两个特权方法添加到Page的原型对象中好了。
function Page(){
var dom;
if(this.__proto__.setDom !== "function"){
this.__proto__.setDom=function(newDom){
dom=newDom;
};
this.__proto__.getDom=function(){
return dom;
};
}
}
对修改后的Page类进行测试:
var page1=new Page();
page1.setDom("<div>page1<div>");
console.log(page1.getDom());//<div>page1<div>
var page2=new Page();
page2.setDom("<div>page2</div>");
console.log(page2.getDom());//<div>page2</div>
/*
注意!问题出现了!
*/
console.log(page1.getDom());//<div>page2<div>
这时最后一行的console.log(page1.getDom())
打印的结果变成了<div>page2<div>
探究
前提
在使用
new
操作符调用Page
构造函数创建对象的过程:先创建一个Page
类的空对象,并将构造函数中的this
指向该对象,再执行构造函数。在函数中定义的变量都保存在该函数执行环境的变量对象中。
每个函数都有一个作用域链。函数在定义时,就会生成不完整的作用域链,该作用域的前端是函数定义时所在环境的变量对象。函数在执行时,会创建该函数的执行环境,并将该执行环境的活动对象(这里可理解为变量对象)添加到之前创建的作用域链的前端,此时的作用域链是完整的作用域链。
变量访问的过程就是从作用域链家前端沿着作用域链查找变量的过程。
分析
Page
类的构造函数使用动态原型的方式来给原型对象添加方法。因此在创建第一个Page
实例时,调用Page
函数创建执行环境a,这时,会在a中定义setDom
和getDom
方法并添加到Page
类的原型对象中。原型对象中的setDom
和getDom
方法的作用域链前端都是执行环境a(准确的说是a对应的变量对象)。创建第一个
Page
实例时,给Page
类的原型对象添加了setDom
和getDom
方法。之后再创建其他的Page
实例时,都不会再修改原型对象中的setDom
和getDom
方法了。在创建第二个
Page
实例时,再次调用Page
函数,创建了执行环境b。在调用
Page
实例的setDom
或getDom
方法时,由于都是调用原型对象中的方法。而根据之前说的,原型对象中的setDom
方法和getDom
方法在访问dom
变量的时候,都要沿着作用域链查找,最后找到的是环境a中的变量dom
,也就是第一个Page
实例的私有变量dom
。所以在测试的时候,无论是
page1
还是page2
调用setDom
和getDom
方法,都是修改或访问page1
中的私有变量dom
。
小结
在类的原型对象中添加的特权方法,不能用来访问或操作类的私有变量,只能用来访问或操作this
修饰的特权变量。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。