JS函数表达式+

模仿块级作用域

Javascript,没有块级作用域,在块语句中定义的变量,实际上是包含在函数中而非语句中创建

function outputNumbers(count){
   for(var i=0;i<count;i++){
      alert(i);   
   }
   alert(i);
}

在Javascript中,变量i是定义在outputNumbers()的活动对象中,因此从它有定义开始就可以在函数的随从访问它。即使错误的重新声明同一个变量,也不会改变它的值。

function outputNumbers(count){
   for(var i=0;i<count;i++){
      alert(i);   
   }
   alert(i);
}
var i;
alert(i);

Javascript不会提示多次声明一个变量,它只会对后续的声明视而不见(会执行后续声明中的变量初始化)匿名函数可以用模仿块级作用域避免这个问题。

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

定义并立即调用一个匿名函数。将函数声明包含是在一对圆括号中,表示它实际上是一个函数表达式,后面的括号表示立即调用这个函数。

var tFunction = function(){
   //块级作用域
}
tFunction();

先定义一个函数,然后调用它。函数定义是创建匿名函数并赋值给tFunction。

function outputNumbers(count){
   (function(){
       for(var i=0;i<count;i++){
         alert(i);   
      }
   })();
   alert(i);//错误
}

上面代码在for循环外部插入了一个私有作用域,在匿名函数中定义的任何变量,在执行结束时被销毁。因此i只能在循环中使用,而匿名函数访问count是因为匿名函数是一个闭包,可以访问包含作用域中的所有变量。
这种技术通常用于限制向全局作用域中添加过多的变量和函数。在一个大型应用程序中,过多的全局变量和函数容易造成命名冲突,创建是有作用域就不必担心搞乱全局作用域。
这种做法减少闭包占用内存,因为没有指向匿名函数的引用,所以执行完毕后就销毁其作用域链了

私有变量

在Javascript中没有私有成员的概念;所有对象属性都是共有的。不过有私有变量的概念。任何函数中的变量都是私有变量,在函数外部不能访问。是有变量包括函数的参数,局部变量个和内部定义的其他函数

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

函数内部可以访问num1,num2,sum。而在外部则不可以访问,如果在函数内部创建一个闭包这可以通过自己的作用域链访问这些变量,这样就可以创建访问私有变量的公有方法。这种方法被称为特权方法

function MyObject(){
   var privateVariable = 10;
   fucntion privateFunction(){
      return false;
   }
   //特权方法
   this.publicMethod = function (){
      privateVariable++;
      return privateFunction();
   };
}

创建MyObject实例后只能使用publicMethod()访问privateVariable和privateFunction()。每次调用构造函数会创建不同的私有变量,也会重新创建特权方法。(使用静态私有变量来实现特权方法就可以避免这个问题)

静态私有变量

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

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

创建一个私有作用域。在私有作用域中,首先定义私有变量和私有函数,然后定义构造函数及其公有方法。公有方法是在原型上定义的。由于函数声明只能创建局部函数,同样也不使用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";

在一个实例上调用setName()会影响所有的实例。而调用setName()或新建一个Person实例都会赋予name属性一个新值。结果就是所有实例都会返回相同的值。
这样因为使用原型而增强代码复用,但每个实例都没有自己的私有变量,如何使用还要视情况而定。
多查找作用域链中的一个层次,就会在一定程度上影响查找速度。这是使用闭包和私有变量的不足之处

模块模式

为单例创建私有变量和特权方法。单例就是一个实例的对象。Javascript是以对象字面量的方式创建单例对象的

var singleton = {
   name : value,
   method : function(){
      //方法代码
   } 
};

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

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);
         }
      }
   };
}();

这个简单的例子创建了一个用于管理组件的application对象。在创建这个对象的过程中,首先声明一个私有的components数组,并向数组中添加一个BaseComponent的新实例而返回的对象 getComponentCount返回已注册的组件数目registerComponent 用于注册新组建。
以这种模式创建的每个单例都是Object的实例,因为最终要通过一个对象字面量来表示它。

增强的模块模式

在返回对象之前加入对其增强的代码,这种模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性和方法对其加以增强的情况。

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 BaseCponent();
   //公共接口
   app.getComponentCount = function(){
      return components.length;
   };
   app.registerComponent = function(component){
      if(typeof component == "object"){
         components.push(component);
      }
   };
   return app;
}();

首先定义私有变量。因为app必须是BaseComponent的实例,实际是application对象的局部变量版,之后为app对象添加能够访问私有变量的公有方法,最后返回app对象,结果将他赋值给全局变量application。

推荐阅读更多精彩内容