JS 7.3-7.4 模仿块级作用域&私有变量

7.3 模仿块级作用域

JavaScript从来不会告诉你是否多次声明了同一个变量;

只会对后续的声明视而不见,不过,它会执行后续声明中的变量初始化;

匿名函数可以用来模仿块级作用域并避免这个问题;

用作块级作用域也叫私有作用域的匿名函数的语法如下;

(function(){
//这里是块级作用域
})();

一个更具体的例子;

function outputNumbers(count){
    (function () {
        for (var i=0; i < count; i++){
            alert(i);
        }
    })();
    alert(i); //导致一个错误!
}

这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数;

(function(){
    var now = new Date();
    if (now.getMonth() == 0 && now.getDate() == 1){
        alert("Happy new year!");
    }
})();

假如在一个共同开发的大型应用中,这样就避免了命名冲突;

同时,这样可以减少闭包占用的内存问题,因为没有指向匿名函数的引用;

只要函数执行完毕,就可以立即销毁其作用域链了;

7.4 私有变量

特权方法

严格来讲,js中没有私有成员的概念,所有对象属性都是公有的;

但是有私有变量的概念,有权访问私有变量和私有函数的公有方法叫特权方法;

function MyObject(){
//私有变量和私有函数
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
//特权方法
    this.publicMethod = function (){
        privateVariable++;
        return privateFunction();
    };
}

利用私有和特权成员,可以隐藏那些不应该被直接修改的数据;

function Person(name){
    this.getName = function(){
        return name;
    };
    this.setName = function (value) {
        name = value;
    };
}
var person = new Person("Nicholas");
alert(person.getName()); //"Nicholas"
person.setName("Greg");
alert(person.getName()); //"Greg"

缺点:必须使用构造函数模式来达到这个目的;

构造函数模式的缺点是针对每个实例都会创建同样一组新方法,

而使用静态私有变量来实现特权方法就可以避免这个问题;

静态私有变量

在私有作用域中定义私有变量或函数来创建特权方法;

(function(){
//私有变量和私有函数
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
//构造函数
    MyObject = function(){
    };
//公有/特权方法
    MyObject.prototype.publicMethod = function(){
        privateVariable++;
        return privateFunction();
    };
})();

没有var关键字,MyObject是一个全局变量;

严格模式下这样会报错;

使用函数表达式而没有使用函数声明是因为函数声明只能创建局部函数;

(function () {
    var name = "";
    Person = function (value) {
        name = value;
    };
    Person.prototype.getName = function () {
        return name;
    };
    Person.prototype.setName = function (value) {
        name = value;
    };
})();
var person1 = new Person("Nicholas");
alert(person1.getName()); //"Nicholas"
person1.setName("Greg");
alert(person1.getName()); //"Greg"
var person2 = new Person("Michael");
alert(person1.getName()); //"Michael"
alert(person2.getName()); //"Michael"

name变成了一个静态的所有实例共享的属性;

以这种方式创建静态私有变量会因为使用原型而增进代码复用,但每个实例都没有自己的私有变量;

到底是使用实例变量,还是静态私有变量,最终还是要视你的具体需求而定;

多查找作用域链中的一个层次,就会在一定程度上影响查找速度;

而这正是使用闭包和私有变量的一个显明的不足之处;

模块模式

模块模式是为单例创建私有变量和特权方法;

var singleton = function(){
//私有变量和私有函数
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
//特权/公有方法和属性
    return {
        publicProperty: true,
        publicMethod : function(){
            privateVariable++;
            return privateFunction();
        }
    };
}();

增强的模块模式

适用于单例必须是某种类型的实例

var singleton = function(){
//私有变量和私有函数
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
//创建对象
    var object = new CustomType();
//添加特权/公有属性和方法
    object.publicProperty = true;
    object.publicMethod = function(){
        privateVariable++;
        return privateFunction();
    };
//返回这个对象
    return object;
}();

假如application必须是BaseComponent的实例

var application = function () {
//私有变量和函数
    var components = new Array();
//初始化
    components.push(new BaseComponent());
//创建application 的一个局部副本
    var app = new BaseComponent();
//公共接口
    app.getComponentCount = function () {
        return components.length;
    };
    app.registerComponent = function (component) {
        if (typeof component == "object") {
            components.push(component);
        }
    };
//返回这个副本
    return app;
}();

这个实例实际上是application对象的局部变量版;

此后,我们又为app对象添加了能够访问私有变量的公有方法;

最后一步是返回app对象,结果仍然是将它赋值给全局变量application