var functionName=function(arg0,arg1,arg2){
//函数体
};
alert(functionName.name);// functionName
这种情况下创建的函数叫做匿名函数(拉姆达函数)匿名函数的name属性时空字符串。
7.1 递归
递归函数是通过名字调用自身的情况下构成的
function factorial(num){ if(num<=1){return 1; } else{ return num*factorial(num-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);
}
});
7.2 闭包
闭包是指有权访问另一个函数作用域中的变量的函数。常见方式是在函数内部创建另一个函数。
闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多。
7.2.1 闭包与变量
作用域链的配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。例如以下例子每个函数都返回10.
function creatFunctions(){ var result=new Array(); for(i=0;i<10;i++){ result[i]=function(){ return i; } }; return result; } |
可以改为,则返回正常结果
function creatFunctions(){ var result=new Array(); for(i=0;i<10;i++){ result[i]=(function(){ return i; })(); } return result; } |
7.2.2 关于this对象
在全局函数中,this指向window,但函数作为某个对象的方法调用时,this等于那个对象。不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window。
var name=”the window”; var object={ name: “My object”; getNameFunc: function(){ return function(){ return this.name; }; } } alert(object.getNameFunc()());//the window | var name=”the window”; var object={ name: “My object”; getNameFunc: function(){ var that=this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());//My object |
7.2.3内存泄漏
如果闭包的作用域链中保存着一个HTML元素,那么久意味着该元素无法被销毁。
7.3 模仿块级作用域
JavaScript没有块级作用域的概念。变量在函数内部定义后就可以在函数内部随处访问他。
function outputNambers(count){ for (var i=0; i<count;i++){ alert(i); } alert(i);//计数 }
| function outputNambers(count){ for (var i=0; i<count;i++){ alert(i); } var i;//重新声明变量,对结果没影响 alert(i);//计数 } |
匿名函数可以用来模仿块级作用域
function outputNambers(count){ (function(){ for (var i=0; i<count;i++){ alert(i); } })();//创建一个函数并立即调用,调用完其定义的变量会立即销毁 alert(i);//导致一个错误 } |
这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域链。
7.4 私有变量
任何函数定义的变量都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
function add(num1,num2){
var sum=num1+num2;
return sum;
}// 有三个私有变量: num1、num2和sum.
特权方法:有权访问私有变量和私有函数的共有方法称为特权方法。即在函数外部,只有通过调用特权方法才能访问函数内部的私有变量或私有函数。
function MyObject(){ //私有变量和私有函数 var privateVatiable=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 person1=new Person(“YaoMing”); alert(person1.getName());//YaoMing person1.setName(“Greg”); alert(person1.getName());//Greg |
7.4.1 静态私有变量
(function(){ //私有变量 var name=””; //构造函数,没有使用函数声明,Person是一个全局变量。 Person=function(value){ name=value; }; //公有、特权方法。作为一个闭包,总是包含对作用域的引用。在原型上定义的,所有实例都是用同一个函数 Person.prototype.getName=function(){ return name; }; Person.propotype.setName=function(value){ name=value; }; })(); var person1=new Person(“Nicholas”); person1.setName(“Greg”); alert(person1.getName);//Greg var person2=new Person(“Michael”); alert(person1.getName);//Michael alert(person2.getName);//Michael |
这个例子中Person构造函数与getName()和setName()方法一样都有权访问name。在这种模式下,name就变成了一个静态的、由所有实例共享的属性。
以这种方式创建静态私有变量会因为使用原型而增进代码复用,但每个实例都没有自己的私有变量。
7.4.2 模块模式
前面所说的方法是为自定义类型创建私有变量和特权方法,而道格拉斯所说的模块模式则是为单例创建私有变量和特权方法。这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有效的。
JavaScript中是以字面量的方式来创建单例对象的。
var singleton={
name: value;
method: function(){
//这里是方法代码
};
}
模块模式通过为单例添加私有变量和特权方法能够使其得到加强,语法如下:
var singleton=function(){ //私有变量和函数 var privateV=10; function privateFunction(){ return false; } //返回对象字面量只包含可以公开的属性和方法,这个对象字面量定义是单例的公共接口。 return{ publicProperty: true; publicMethod: function(){ privateV++; return privateFunction(); } }; }(); |
如果必须创建一个对象并对某些数据进行初始化,同时还要增加一些能够访问这些私有数据的方法,那么就可以使用这种模式。
7.4.3增强的模块模式
进一步改进了模块模式,即在返回对象之前加入对其增强的代码。这种增强的模块模式适合那些单例必须是某种类型的实例,同时还必须添加某些属性或方法对其加以增强的情况
var singleton=function(){
//私有变量和函数
var privateV=10;
function privateFunction(){
return false;
}
var object=new CustomType();
object.publicProperty: true;
object.publicMethod: function(){
privateV++;
return privateFunction();
};
return object;
}();