7.4 私有变量


严格来讲,JavaScript并没有什么私有变量的概念,但是从作用域的角度来看,一个是全局,函数; 在函数中定义的变量都可以称之为 私有变量;因为不能在函数外部访问;

1 function add(num1,num2){
2   var sum = num1 + num2;
3   return sum;
4 }

 

  上面代码完成两个数字累加,函数内部声明三个私有变量,num1, num2, sum. 在函数内部可以访问这几个变量,但函数外部则不能访问它们。

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

 

在js中,有权访问私有变量和函数的公有方法称为特权方法。 有两种在对象上创建特权方法的方式。

  构造函数中定义特权方法

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

var obj1 = new MyObject();
var obj2 = new MyObject();
alert(obj1.publicMethod()); // 11
alert(obj2.publicMethod()); // 11

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

  在构造函数中定义特权方法也有一个缺点,那就是你必须使用构造函数模式来达到这个目的。 构造函数模式的缺点是针对每个实例都会创建同样一组新方法,而使用静态私有变量来实现特权方法就可以避免这个问题。

7.4.1 静态私有变量

  通过在私有作用域中定义私有变量或函数,同样也可以创建特权方法,其基本模式如下

  

 1 (function(){
 2   //私有变量和私有函数
 3   var privateVariable = 10;
 4   function privateFunction(){
 5     return false;
 6   }
 7   //构造函数
 8   MyObject = function(){
 9     
10   }
11   //公有 / 特权方法
12   MyObject.prototype.publicMethod =   function(){
13      privateVariable++;
14      return privateFunction();
15   };
16   
17 })();
18 
19 var ii = new MyObject();
20 console.log(ii.publicMethod()); // false

   这种原型模式区别于构造函数模式,在于私有变量 和 特权方法是所有实例共用的。来看看下面

 1 (function(){
 2   //私有变量和私有函数
 3   var privateVariable = 10;
 4   function privateFunction(){
 5     return privateVariable;
 6   }
 7   //构造函数
 8   MyObject = function(){
 9     
10   }
11   //公有 / 特权方法
12   MyObject.prototype.publicMethod =   function(){
13      privateVariable++;
14      return privateFunction();
15   };
16   
17 })();
18 
19 var ii0 = new MyObject();
20 
21 var ii1 = new MyObject();
22 console.log(ii0.publicMethod()); //11
23 console.log(ii1.publicMethod()); //12

  公有方法定义在原型上,所有实例共享。而特权方法作为一个闭包,总是保存着外部函数的作用域的引用。在这种模式下,变量 privateVariable 就变成了一个静态的、由所有实例共享的属性。也就是说,在一个实例上调用setName 会影响所有实例。

  以这种方式创建静态私有变量,会因为使用原型而增强代码复用,但是由前面原型模式中介绍过,这样每个实例中就没有自己的私有属性(实例变量)可言。

PS: 多查找作用域链中的一个层次,就会在一定程度上影响查找速度。而这正是使用闭包和私有变量的一个明显的不足之处。

 

 7.4.2 模块模式

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

  JavaScript 是以对象字面量的方式来创建单例对象的。

 

1  var singleton = {
2 
3     name:value,
4     method:function(){
5       //dosomething
6     }
7   }

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

 1 var singleton = function(){
 2 
 3     //私有变量和私有函数
 4     var privateVariable = 10;
 5     function privateFunction(){
 6 
 7       return false;
 8     }
 9 
10     return {
11 
12       publicPrototype:true,
13       publicMethod :function(){
14         privateVariable++;
15         return privateFunction();
16       }
17     }
18   }

这个模块模式使用了一个返回对象的匿名函数。在这个匿名函数内部,首先定义了私有变量和函数。然后,将一个对象字面量作为函数的值返回。

返回的对象字面量中只包含可以公开的属性和方法。由于这个对象是在匿名函数内部定义的,因此它的公有方法有权访问私有变量和函数。

从本质上来讲,这个对象字面量定义的单例的公共接口。

这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的,

例如:

 1 function BaseComponent(){
 2   //构造函数
 3 }
 4 var appliction = function(){
 5   //私有变量和函数
 6   var components = new Array();
 7   
 8   //初始化
 9   components.push(new BaseComponent());
10   
11   //公共
12   return {
13     getComponentCount :function(){
14       return components.length;
15     },
16     registerComponent:function(component){
17         if(typeof component =="object"){
18           components.push(component);
19         }
20       
21     }
22   }
23 }

对于上面来说,以这种模式创建的每个单例都是Object的实例。因为最终要通过一个对象字面量来表示它。

使用场合:

  如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么久可以使用模块模式。

7.4.3 增强的模块模式

  在上面的基础上对其增强的代码。这种增强模式,适合那些单例必须是某种类型的实例,同时必须添加某些方法和属性对其增强的情况。

  

 1 var singleton = function(){
 2 
 3     var privateVariable = 10;
 4     function privateFunction(){
 5       return false;
 6     }
 7     //创建对象
 8     var object = new CustomType();
 9     //添加特权/ 公有属性和方法
10     object.publicProperty = true;
11     object.publicMethod = function(){
12       privateVariable++;
13       return privateFunction();
14     }  
15     return object;
16   }

如果是前面appliction对象必须是BaseComponent的实例,那么使用以下代码

 1 var appliction = function(){
 2   //私有变量和函数
 3   var components = new Array();
 4   
 5   //初始化
 6   components.push(new BaseComponent());
 7   
 8   var app = new BaseComponent();
 9   //公共
10  
11   app.getComponentCount  = function(){
12     return components.length;
13   }
14   app.registerComponent = function(component){
15     if(typeof component =="object"){
16       components.push(component);
17     }
18   }
19   //返回这个副本
20   return app;
21 }

这代码与上面模块模式最大不同,就是返回的单例指定了特定的类型。

 

构造函数模式、原型模式来实现自定义类型的特权方法;

模块模式、增加模式来实现单例的特权方法;

posted @ 2016-07-03 22:07  czhyuwj  阅读(195)  评论(0编辑  收藏  举报