许多编程语言使用私有变量,这些私有变量是对外部隐藏的对象属性。这是非常有用的一种特性,因为当通过其他代码访问这些代码时,我们不希望对象的实现细节对用户造成过度符合.遗憾的是,原生JavaScript不支持私有变量。
但是,通过使用闭包,我们可以实现很接近的,可接受的私有变量,示例代码如下面例子所示。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../unitl/test.js"></script>
<style>
#results li.pass {color:green;}
#results li.fail {color:red;}
</style>
</head>
<body>
<ul id="results"></ul>
</body>
<script>
// 定义ninja构造函数。
function Ninja() {
//在构造函数内部声明一个变量,因为所声明的变量的所有域局限于构造函数的内部,所以它是一个“私有”变量。我们使用该变量统计ninja佯攻的次数。
var feints = 0;
//创建用于访问计数变量feints的方法。由于在构造函数外部的代码是无法访问feints变量的,这是通过办读形式访问该变量的常用方法。
this.getFeints = function() {
return feints;
};
//为feints变量声明一个累加方法。由于feints就私有变量在外部是无法累加的,累加过程则被限制在我们提供的方法中。
this.feint = function() {
feints++;
};
}
// 现在开始测试,首先创建一个ninja的示例
var ninja1 = new Ninja();
//调用feint方法,通过该方法增加ninja的佯攻次数。
ninja1.feint();
//验证我们无法直接获取该变量值。
assert(ninja1.feints === undefined,"Add the private data is inaccessible to us.");
//虽然我们无法直接对feints变量赋值,但是仍然能够通过getFeints方法操作变量的值。
assert(ninja1.getFeints()===1,"We are to access the internal feint count.");
//当我们通过ninja构造函数创建一个新的ninja2实例时,ninja2对象则具有自己私有的feints变量。
var ninja2 = new Ninja();
assert(ninja2.getFeints() === 0, "the second ninja object gets its own feints variable.");
</script>
</html>
我们创建了一个Ninja构造器。通过在函数上使用关键字new时,就会创建一个新的对象实例,此时调用构造函数,将新的对象作为它的上下文,所以,函数内的this将指向新的实例化对象。
在构造器内部,我们定义了一个变量feints用于保存状态。由于javascript的作用域规则的限制,因此只能在构造内部访问该变量。为了让作用域外部的代码能够访问该变量,我们定义了访问该变量的方法getFeints。为了让作用域外部的代码能够访问私有变量,
我们定义了访问该变量的方法getFeints。该方法可以读取私有变量,但不能改写私有变量。(只读访问的方法通常称为"getter")。
function Ninja() {
var feints = 0;
this.getFeints = function() {
return feints;
},
this.feint = function() {
feints++;
}
}
接下来创建增量方法feint,用于控制私有变量的值。在真实的应用程序中,该方法可能是一些业务逻辑的处理方法,但是在本例中,它只增加变量feints的值。
在构造器完成了它的使命后,我们新建ninja1实例,并调用ninja1的实例方法feint:
var ninja1 = new Ninja();
ninja1.feint();
通过测试显示,我们可通过闭包内部方法获取私有变量,但是不能直接访问私有变量。但有效地阻止了读私有变量不可控的修改,这与真实的面向对象语言的私有变量一样。这种情况如图
所示。
在构造器中隐藏变量,使其在外部作用域不可访问,但是可在闭包内部访问。
- 通过变量ninja,对象实例是可见的。
- 因为feint方法在闭包内部,因此可以访问feints。
- 在闭包外部,我们无法访问变量feints。
通过使用闭包,可以通过方法对ninja的状态进行维护,而不允许直接访问---这是因为闭包内部的变量可以通过闭包内的方法访问,构造器外部的代码则不能访问闭包内部的变量。