我们之前通过各种构造模式构建js对象,都是直接将属性添加在对象上或者对象的原型上。这样并不满足面向对象的封装思想,随便一个人只要使用o.property就能改变我们对象的属性,而且想怎么改怎么改。这就让我们有了创建私有变量的动力。
私有变量
怎么创建私有变量?首先我们要知道,函数内部的变量(使用var定义)不能被函数外部访问到,这就是函数的私有变量。那我们可以利用这一点来创建有私有变量的对象:在函数内部创建一个闭包,闭包引用了函数的私有变量。那么在函数外部可以通过闭包来访问这个私有变量,这样就实现了封装。
function MyObject() {
var privateVariable = 10;
function privateFunction() = {
return false;
}
this.publicMethod = function() {
privateVariable++;
return privateFunction();
}
}
这样本来函数内部的私有变量和私有方法我们是访问不到的,但是通过定义在对象上的公有方法我们就可以有了访问私有变量的接口。这不正是面向对象思想指引我们要做的嘛。
静态私有变量
那么现在又回到了一个共同的问题上,我们想要什么样的对象?通过这种方法创建的对象是有了私有变量,这很好,可是我们之前想过,我们一般想要的对象是每个对象都有自己独立的属性,互不影响,而方法则大家共享避免不必要的浪费。
那么回头看一下上面创建私有变量的访问接口过程,发现我们创建的每一个对象都有自己的方法,这太重复了,那我们怎么解决这个问题呢?我们知道可以把方法定义在原型上,这样所有的实例就可以共享方法了。
(function() {
var privateVariable = 10;
function privateFunction() {
return false;
}
MyObject = function() {
}
MyObject.prototype.publicMethod = function() {
privateVariable++;
return privateFunction();
}
})();
我们知道java中静态变量即static定义的变量会被所有类的对象共享,我们上述方法创建的对象都有一个publicMethod方法,这是共享的,这个方法对变量操作,这个变量也是大家共享的。我们发现虽然我们实现了方法共享,可是不小心变量也共享了。这个变量就是静态私有变量。
这并不是我们想要的,难道就这么难吗?想要私有变量就不能定义属性在对象上面,只能通过对象上面的方法来操作这个变量,同时不能让人家直接操作这个属性变量,所以一切都要在私有作用域中进行,然后全世界就只有一种办法访问这个属性变量,就是那个公有的方法。
可是不在对象上面定义属性变量,又要让方法在对象原型身上,原型的方法引用这个变量,这个变量一定会被所有的实例共享的啊!也就是一定是静态的私有变量咯。那我们要创建每个实例都有自己的属性只能牺牲一下让每个实例都有一个公有方法了。浪费就浪费吧,谁让我们想要私有变量呢。
模块模式
虽然我们做不到想要的那样,但是有时候我们只需要产生一个实例,不是千千万万个实例,也就是单例。这种情况下我们就可以使用构造函数来创建私有变量了(第一种方式),可是我们既然是单例,就不需要构造函数了。所以我们一般创建单例都是这样来的:
var singleton = {
name: value,
method: funciton() {
return false;
}
}
当我们想创建一个单例的时候就这么来,可是我们又想创建有私有变量的单例,和前面的构造函数创建私有变量结合起来,我们可以得到有私有变量的单例:
var singleton = function(){
var privateVariable = 10;
function privateFunction() {
return false;
}
return {
publicProperty: true,
publicMethod: function() {
privateVariable++;
return privateFunction();
}
};
}();
你可以看到我们通过在函数作用域内返回一个引用了函数私有变量的单例,得到了一个单例对象。这其实和构造函数一样的道理,不过就特殊在单例而已。
增强的模块模式
我们通过上述模块模式产生的单例都是Object的实例,大多数情况下这没什么,我们不会需要去判断单例对象到底是谁的实例,但是有时候就是那么操蛋,我们可能真的需要给这个单例加上一个标签,然后typeof singleton来得到它是不是我们想要的那一类单例。这时候就需要把上述模块模式增强一下:
var singleton = function(){
var privateVariable = 10;
function privateFunction() {
return false;
}
var o = new Person();
o.publicProperty = true,
o.publicMethod = function() {
privateVariable++;
return privateFunction();
};
return o;
}();
这样我们产生的单例就是Person对象的实例了。