本文深入解析JavaScript闭包机制,通过对比普通对象属性与函数内部变量的访问权限,展示如何利用闭包实现私有变量。文章详细演示了通过立即执行函数和构造函数模式隐藏状态,确保数据不被外部非法修改,同时保持内部方法对私有状态的长期访问能力。
这种机制的核心优势在于,内部函数能够直接访问其定义时所处的外部函数的参数与变量。
对象属性访问的局限性
首先,我们构建一个基础的对象示例。
具体实现代码如下:
var testObj = {
value: 10,
add: function(inc){
this.value += (typeof inc === "number") ? inc : 1;
}
};
testObj.add();
testObj.value; // 11
testObj.add(2);
testObj.value; // 13上述写法存在一个潜在问题:value属性值无法防止被外部非法修改,因此需要采用以下改进方案。
改进后的代码如下:
var testObj = (function(){
var value = 10;
return {
add: function(inc){
value += (typeof inc === "number") ? inc : 1;
},
getValue: function(){
return value;
}
};
})();
testObj.add();
testObj.getValue(); // 11
testObj.add(2);
testObj.getValue(); // 13利用闭包实现私有变量
我们可以通过调用一个函数来初始化testObj,该函数返回一个对象字面量。函数内部定义的value变量对add和getValue方法始终可用,但受函数作用域限制,对外部程序不可见。此外,这引出一个重要结论:内部函数的生命周期通常比其外部函数更长。
接下来,我们继续分析一个涉及构造函数调用的示例。
相关代码如下:
var MyObj = function(str){
this.status = str;
};
MyObj.prototype.getStatus = function(){
return this.status;
};
var obj = new MyObj("javascript");
obj.getStatus(); // "javascript"构造函数模式的改进与闭包应用
这种写法虽然逻辑正确,但略显冗余。既然status属性可以直接访问,为何还要通过getStatus方法间接访问?只有当status作为私有属性时,这种封装才有实际意义。
优化后的代码如下:
var obj = function(status){
return {
getStatus: function(){
return status;
}
};
};
var myObj = obj("javascript");
myObj.getStatus(); // "javascript"当调用obj时,它返回一个包含getStatus方法的新对象,该对象的引用被保存在myObj中。即使obj函数执行完毕并返回,getStatus方法依然享有访问obj函数status参数的特权。需要注意的是,getStatus访问的并非参数的副本,而是参数本身。之所以能实现这一点,是因为该函数可以访问其创建时所处的上下文环境,这种现象即被称为闭包。