严格讲的话,在JavaScript没有私有成员的概念,因为所有对象属性都是公开的。不过,一般在函数中定义的变量,可以认为是私有变量,因为不能在函数外部访问这些变量,私有变量包括参数、局部变量和在函数内部定义的其他函数。
如果在函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问函数的私有变量,因此可以创建用于访问私有变量的公有方法,一般称有权访问私有变量和私有函数的公有方法为特权方法。创建特权方法有两种方式,一是在构造函数中定义,二是使用原型模式实现。
构造函数中定义的方式如下:
function MyFunction(){
// 私有变量和函数
var privateNum = 1;
function privateFunction(){
return true;
}
// 特权方法
this.publicFunction = function(){
privateNum + 1;
return privateFunction();
};
}
能在构造函数中定义特权方法,是因为特权方法作为闭包有权访问在构造函数中定义的所有变量和函数。在创建MyFunction的实例后,除了使用publicFunction()外,没有其他方法可以直接访问privateNum和privateFunction();私有和特权方法常用于隐藏不应该被直接修改的数据,如下例子:
function User(age){
this.getAge = function(){
return age;
}
this.setAge = function(value){
age = value;
}
}
var user = new User(18);
alert(user.getAge()); // 18
user.setAge(19);
alert(user.getAge()); // 19
getAge()和setAge()是两个特权方法,这两个方法都可以在构造函数外部使用,这两个方法作为闭包能通过作用域链访问age。
在构造函数中定义特权方法的方式,会对每个实例都创建同一组新方法,比如上面例子中,age在每个User实例中都不相同,因为每次调用构造函数都会重新创建两个特权方法。因此引出使用原型模式实现特权方法。
原型模式实现特权方法的方式如下:
(function(){
// 私有变量和函数
var privateNum = 1;
function privateFunction(){
return true;
}
// 构造函数
MyFunction = function(){};
// 特权方法(公有)
MyFunction.prototype.publicFunction = function(){
privateNum + 1;
return privateFunction();
};
})();
以上的特权方法是在原型上定义的,这里要注意的是在定义构造方法时不是使用函数声明的方式,而是使用函数表达式,因为函数声明只能创建局部函数,同理,在声明MyFunction也没有使用var关键字,因为初始化未经声明的变量,是创建一个全局变量。(注:如果是在严格模式下,给未经声明的变量赋值会导致错误)
使用原型模式与在构造函数中定义特权方法的区别在于私有变量和函数是由实例共享的,由于特权方法是在原型上定义的,因此所有实例都使用同一个函数。特权方法作为闭包,总是保存有对包含作用域的引用。
(function(){
var age = "";
User = function(value){
age = value;
};
User.prototype.getAge = function(){
return age;
};
User.prototype.setAge = function(value){
age = value;
};
})();
var user1 = new User("18");
alert(user1.getAge()); // "18"
user1.setAge("19");
alert(user1.getAge()); // "19"
var user2 = new User("20");
alert(user2.getAge()); // "20"
alert(user1.getAge()); // "20"
这种模式下,变量age就变成了一个静态的、由所有实例共享的属性,一个实例调用setAge()会影响所有实例。
至于实际中使用哪种方式定义特权方法还要视需求来定。
——顾故