本文详解 JavaScript 中的特权方法模式,通过闭包机制确保对象属性的真正私有化。文章对比了不使用 new 的关键字调用与构造函数调用两种方式,指出后者会导致属性公开。通过实例展示如何利用 that 载体和共享变量实现属性的安全读写与继承扩展。
特权方法模式与私有属性封装
该模式的核心价值在于实现对象属性的严格私有化。外部代码无法直接访问或修改对象的内部状态,所有的交互必须通过预先定义的特权方法进行。
为了更直观地理解,下面提供一个具体的代码示例。
具体实现代码如下:
var person = function(cfg) {
var that = {};
that.getName = function() {
return cfg.name || 'unknow name';
};
// 性别 默认男性
that.getGender = function() {
return cfg.gender || 'male';
};
return that;
};
var programmer = function(cfg) {
var that = person(cfg),
share = {};
share.status = 'normal';
that.getFamiliarLanguage = function() {
return (cfg.langs || []).join(' ');
};
that.getProfile = function() {
return 'hi,my name is ' + that.getName();
};
that.getStatus = function() {
return share.status;
};
that.setStatus = function(status) {
share.status = status;
};
return that;
};
var me = programmer({
name: 'AndyZhang',
gender: 'male',
// 熟悉的语言
langs: ['javascript', 'java', 'php']
});
console.debug(me.getFamiliarLanguage());
console.debug(me.getProfile());
me.setStatus('oh really busy..');
console.debug(me.getStatus()); 构造函数调用与私有化的冲突
观察上述代码可以发现,调用 programmer 函数时并未使用 new 关键字,且函数内部也未出现 this 关键字。
如果代码中类似 this.name = cfg.name 这样的属性赋值存在,并采用 new 关键字进行构造函数调用,生成的对象属性将变为公开状态,不再具备私有特性。例如:
代码如下:
// 这里方法名的首字母大写 以表示作为一个构造方法 用new来调用
var Person = function(cfg) {
this.name = cfg.name;
this.gender = cfg.gender;
}
// new出一个person1
var person1 = new Person({
name: 'Andrew',
gender: 'male'
});
// 本来是想让name, gender成为私有 用类似setter getter方法来读写 就像javaBean一样
alert(person1.name); // 'Andrew'
alert(person1.gender); // 'male' 私有属性约定的替代方案
从上述示例可以看出,person1 对象的属性可以直接被外部访问,未能实现真正的私有保护。在实际开发中,有时会通过代码规范或约定来标识私有属性,例如使用 this._name 表示 name 为私有属性。只要团队统一约定,这也是一种可行的方式,在诸如 YUI2 等第三方 JS 库中较为常见。
基于闭包的继承与状态共享
回到最初的代码,由于未使用 this,而是采用 that 作为对象载体。在 programmer 方法中,that 起到了关键作用:调用 person 后返回的 that 已具备 person 的所有方法(如 getName、getGender)。随后,根据 programmer 的具体需求在 that 基础上进行扩展,甚至覆盖原有方法。此外,programmer 中的 share 变量可用于集中管理私有变量和方法。借助 JavaScript 的作用域(scope)和闭包(closure)机制,that 扩展的方法可以访问和处理这些共享数据,例如代码中的 that.getStatus 和 that.setStatus 方法。最后通过 return that 返回对象实例。