JS函数表达式 -- 私有变量

风格不统一 提交于 2020-03-18 06:53:28

  任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。

私有变量包括函数的参数、局部变量和在函数内部定义的其它函数。

function add(num1, num2){
    var sum = num1 + num2;
    return sum;
}

在这个函数中,有三个私有变量: num1、num2和sum。在函数内部可以访问这几个变量。但在函数外部则不能访问他们。

如果在这个函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量。利用这一点,就可以创建用于访问私有变量的公有方法。

我们把有权访问私有变量和私有桉树的公有方法称为特权方法(privileged method)。有两种在对象上创建特权的方法:

1. 在构造函数中定义特权方法:

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

var obj = new MyObject();
alert(obj.publicMethod());    //false

这个模式在构造函数内部定义了私有变量和函数。然后,又继续创建了能够访问这些私有成员的特权方法。

能够在构造函数中定义特权方法,是因为特权方法作为闭包有权访问在构造函数中定义的所有变量和函数。

对这个例子而言,变量privateVariable和函数privateFunction()只能通过特权方法publicMethod()来访问。在obj中,出了使用publicMethod()这一个途径外,没有任何办法可以直接访问privateVariable和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

在构造函数中定义特权方法也有一个缺点: 必须使用构造函数模式来达到这个目的。构造函数模式的缺点是针对每个实例对象都会创建同样一组新方法。

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

2.使用静态私有变量来实现特权方法:

通过在私有作用域中定义私有变量和函数,同样也可以创建特权方法。

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

var obj = new MyObject();
alert(obj.publicMethod());    //false

这个模式创建了一个私有作用域,并在其中封装了一个构造函数及相应的方法。

在私有作用域中,首先定义了私有变量和私有函数,然后有定义了构造函数及其公有方法。公有方法是在原型上定义的,这一点体现了典型的原型模式。

需要注意的是,这个模式在定义构造函数时并没有使用函数声明,而是使用了函数表达式,函数声明只能创建局部函数。出于同样的原因,在声明MyObject时没有使用var关键字。

初始化未经声明的变量,总是会创建一个全局变量。因此MyObject就会成了一个全局变量。

这种模式与在构造函数中定义特权的主要区别,在于私有变量和函数是由实例共享的。而这个特权方法,作为一个闭包,总是保存着对包含作用域的引用。

(function(){
    var name = "";
    
    Person = function(value){
        name = value;
    };
    
    Person.prototype.getName = function(){
        return name;
    };
    Person.prototype.setName = function(value){
        name = value;
    };
})();

var p1 = new Person("Nicholas");
alert(p1.getName());    //Nicholas
p1.setName("Greg");
alert(p1.getName());    //Greg

var p2 = new Person("Michael");
alert(p2.getName());    //Michael
alert(p1.getName());    //Michael

在这个例子中的Person构造函数与getName()和setName()方法一样,都有权访问私有变量name。在这种模式下,变量name就变成了一个静态的、由所有实例共享的属性。也就是说,在一个实例上调用setName()会影响所有实例。

 

3. 模块模式

  前面的模式是用于为自定义类型创建私有变量和特权方法。而模块模式(module pattern)则是为单例创建私有变量和特权方法。

  单例: 指只有一个实例的对象,JavaScript是以对象字面量的方式来创建单例对象的。

模块模式通过为单例添加私有变量和特权方法能够使其得到增强。

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

这个模块模式使用了一个返回对象的匿名函数。在这个匿名函数内部,首先定义了私有变量和函数。然后,将一个对象字面量作为函数的指返回。返回的对象字面量中只包含可以公开的属性和方法。由于这个对象是在匿名函数内部定义的。因此它的公有方法有权访问私有变量和函数。从本质上讲,这个对象字面量定义时单例的公共接口。这种模式在需要对单例进行某些初始化,同时有需要维护其私有变量时是非常有用的:

var application = function(){
    //私有变量和私有函数
    var components = new Array();
    
    //初始化
    components.push(new BaseComponent());
    
    //公共
    return {
        getComponentCount: function(){
            return components.length;
        },
        
        registerComponent: function(component){
            if(typeof component == "object"){
                 components.push(component);
            }
        }
    };
}();

  在web应用程序中,经常需要使用一个单例来管理应用程序级的信息。这个简单的例子创建了一个用于管理组件的application对象。在创建这个对象的过程中,首先声明了一个私有的components数组,并向数组中添加了一个BaseComponent的新实例(在这里不需要关心BaseComponent 的代码,只是用它来展示初始化操作)。而返回对象的getComponentCount()和registerComponent()方法,都是有权访问数组components的特权方法。

  如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。以这种模式创建的每个单例都是Object的实例,因为最终要通过一个对象字面量来表示它。单例通常都是作为全局对象存在的,我们不会将它传递给一个函数。因此,没有必要检查他的对象类型。

4.增强的模块模式

有人进一步改进了模块模式,即在返回对象之前加入对其增强的代码。这种增强的模块模式适合那些单例必须是某种类型、同时还必须添加某些属性和方法对其加以增强的实例。

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

主要的不同之处在于命名变量app的创建过程,因为他必须是BaseComponent的实例。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!