function fuctionName(arg0,arg1,arg2){ //函数体 }
不要这样做
if(condition) function sayHi(){ alert("Hi"); } else{ function sayHi(){ alert("Yo"); } }
ECMAScript中属于无效语句。
可以这样做
var sayHi; if(condition){ sayHi=function(){ alert("Hi"); }; } else{ sayHi=function(){ alert("Yo"); }; }
1.递归
function factorial(num){ if(num<=1){ return 1; } else{ return num*arguments.callee(num-1); } }
但在严格模式无法使用arguments.callee,可以这样做
var factorial = (function f(num){ if(num<=1){ return 1; }else{ return num*f(num-1); } });
这种方式在严格非严格都行得通。
2.闭包
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式就是在一个函数内部创建另一个函数
function createComparisonFunction(propertyName){ return function(object1,object2){ var value1=object1[propertyName]; var value2=object2[propertyName]; if(value1<value2) { return -1; } else if(value1>value2) { return 1; } else{ return 0; } }; }
2.1闭包与变量
function createFunctions(){ var result=new Array(); for(var i=0;i<10;i++){ result[i]=function(){ return i; }; } return result; }
每个函数都返回10,因为每个函数的作用域链中都草村这createFunctions()函数的活动对象,所以他们引用的都是同一个变量i。当createFunctions()函数返回后,变量i的值是10,此时每个函数都引用着保存变量i的同一个变量对象,所以都是10。我们可以通过创建另一个匿名函数强制让闭包的行为符合预期
function createFunctions(){ var result=new Array(); for(var i=0;i<10;i++){ result[i]=function(num){ return function(){ return num; }; }(i); } return result; }
2.this对象
在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。不过匿名函数的执行具有全局性,因此其this对象通常只想window,但有时由于编写闭包的方式不同,这一点可能不会那么明显。
var name="The Window"; var object={ name:"My Object"; getNameFunc:function(){ return fuction(){ return this.name; }; } }; alert(object.getNameFunc()());//"The Window"
由于代码创建了一个全局变量name,有创建了一个包含name属性的对象。这个对象还包含一个方法getNameFunc(),它返回一个匿名函数,而匿名函数又返回this.name。由于getNameFunc()返回一个函数,因此调用object.getNameFunc()()就会立即调用它并返回函数。每个函数在被调用时都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。不过,把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问对象。
var name="The Window"; var object={ name:"My Object"; getNameFunc:function(){ var that=this; return fuction(){ return that.name; }; } }; alert(object.getNameFunc()());//"My Object"
2.3内存泄漏
如果闭包作用域链中保存着一个HTML元素,那么就意味着该元素将无法被销毁。
function assignHandler(){ var element=document.getElementById("someElement"); element.onclick=function(){ alert(element.id); }; }
以上代码创建了一个作为element元素事件处理程序的闭包,而这个闭包则又创建了一个循环引用。由于匿名函数保存了一个对assignHandler()的活动对象的引用,因此就会导致无法减少element的引用数。只要匿名函数存在,element的引用数至少也是1,因此占用的内存就永远不会被回收。
function assignHandler(){ var element=document.getElementById("someElement"); var id=elemt.id; element.onclick=function(){ alert(id); }; element=null; }
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!"); } })();
这段代码可以确定那一天是1月1日,如果到了这一天,就会向用户祝贺新年的消息。其中now现在是匿名函数中的局部变量,而我们不必在全局作用域中创建它。
4.私有变量
严格来讲,JavaScript中没有私有成员的概念;所有对象属性都是公有的。不过,倒是有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内定义的其他函数。
我们把有权访问私有变量和私有函数的公有方法称为特权方法。
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"
setName和getName都是特权方法,但优缺点,就是必须使用构造函数模式来达到这个目的,构造函数模式的缺点就是针对每个实例都会创建同样一组新方法,而使用静态私有变量来实现特权方法可以解决。
4.1静态私有变量
(function(){ //私有变量和私有函数 var privateVariable=10; function privateFunction(){ return false; } //构造函数 MyObject =function(){ }; //公有/特权方法 MyObject.prototype.publicMethod=function(){ privateVariable++; return privateFunction(); }; })();
这个模式创建了一个私有作用域,并在其中封装了一个构造函数及相应的方法。
(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"
4.2模块方式
前面的模式是用于为自定义类型创建私有变量和特权方法的。而道格拉斯所说的模块模式则是为单例创建私有变量和特权方法。所谓单例(singleton),指的就是只有一个实例对象。
var singleton={ name:value, method:function(){ //这里是方法的代码 } };
模块模式通过为单例添加私有变量和特权方法能够使其得到增强
var singleton=function(){ //私有变量和私有函数 var privateVariable=10; function privateVariable(){ return false; } //特权/公有方法和属性 return{ publicProperty:true, publicMethod:fucntion(){ privateVariable++; return privateFunction(); } }; }();
4.3增强的模块模式
var singleton=function(){ //私有变量和私有函数 var pribateVariable=10; function privateFunction(){ return fasle; } //创建对象 var object=new CustomType(); //添加特权/公有属性和方法 object.publicProperty=true; object.publicMethod=function(){ privateVariable++; return privateFunction(); }; //返回对象 return object; }();