函数表达式
匿名函数(拉姆达函数)
创建一个函数并将它赋值给变量functionName
1 |
var functionName=function(arg0,arg1,arg2){ |
函数表达式与其他表达式一样,在使用前必须先赋值。(使用“函数声明”定义的函数有着函数声明的特征)
在把函数当成值来使用的情况下,都可以使用匿名函数
应用:
1. 递归
arguments.callee是一个指向正在执行的函数的指针,可用它来实现对函数的递归调用
1 |
function factorial(num){ |
但在==严格模式==下,不能通过脚本访问arguments.callee。访问这个属性会导致错误
解决方法:使用命名函数表达式来达成相同的结果
1 |
var factorial=(function f(num){ |
以上代码创建了一个名为f()的命名函数表达式,然后将它赋值给变量factorial。即便把函数赋值给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正确完成。
2. 闭包
==闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数==
无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)但是,闭包不同
在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。
1 |
function createComparisonFunction(propertyName){ |
在匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。createComparisonFunction()函数在执行完毕后,其活动对象也不会销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换句话说,当createComparisonFunction()返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会保留在内存中;直到匿名函数被销毁后,createComparisonFunction()的活动对象才会被销毁。()
==由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存==
-
闭包与变量
闭包所保存的是整个变量对象,而不是某个特殊的变量。同时闭包只能取得包含函数中的任何变量的最后一个值
1
2
3
4
5
6
7
8
9
10
11
12function createFunctions(){
var result=new Array();
for(var i=0;i<10;i++){
result[i]=function(){
return i;
};
}
return result;
}
var x=createFunctions();
document.write(x[0]()); //结果为10 并非0
这个函数会返回一个函数数组。但是每个函数都返回10,而不是返回自己的索引值(位置0的函数返回0,位置1的函数返回1)
*原因:* 每个函数的作用域链中都保存着createFunctions()函数的活动对象,所以它们引用的都是同一个变量i。当createFunctions()函数返回后,变量i的值是10,此时每个函数都引用着保存变量i的同一个变量对象,所以在每个函数内部i的值都是10.
改善代码:
1 |
function createFunctions(){ |
以上代码,没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋值给数组。这里的匿名函数有一个参数num,也就是最终的函数要返回的值。在调用每个匿名函数时,传入变量i。在这个匿名函数内部,又创建并返回一个访问num的闭包。这样,result数组中的每个函数都有自己num变量的一个副本,因此就可以返回各自不同的数值了。
-
闭包中的this对象
1
2
3
4
5
6
7
8
9var name="The Window";
var object={
name:"My Object",
getName:function(){
return this.name;
}
};
object.getName(); //"My Object"匿名函数的执行环境具有全局性,因此其this对象通常指向window
1
2
3
4
5
6
7
8
9
10
11
12var name="The Window";
var object={
name:"My Object",
getNameFunc:function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)
方法getNameFunc()返回一个匿名函数,而该匿名函数又返回this.name
结果解析:内部函数在搜索this变量时,只会搜索到其活动对象为止,因此永远不可能直接访问到外部函数的this变量(arguments变量也是如此)
把外部作用域中的this对象保存在一个闭包能够访问的变量里,就可以让闭包访问该对象。
1 |
var name="The Window"; |
3. 模仿块级作用域(私有作用域)
众所周知,JavaScript没有块级作用域的概念
例如:
1 |
function outputNumbers(count){ |
JavaScript从来不会告诉你是否多次声明了同个变量;只会对后续的声明视而不见(但是会执行后续声明中的变量初始化)
匿名函数可以用来模仿块级作用域
1 |
var someFunction=function(){ |
1 |
//简约版 |
如果临时需要一些变量,就可以使用私有作用域。例如:
1 |
function outputNumbers(count){ |
模仿私有作用域经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数
4. 私有变量
任何在函数中定义的变量,都可以认为是私有变量。
私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
特权方法:有权访问私有变量和私有函数的公有方法
创建特权方法一:在构造函数中定义特权方法
==把函数设置成构造函数,通过构造函数创建对象,从而通过对象来访问私有变量。==
1 |
function Person(name){ |
Person构造函数中有两个特权方法,这两个方法都可以在构造函数外部使用,而且都有权访问私有变量name。
注意:私有变量name在Person的每一个实例中都不相同,因为每次调用构造函数都会重新创建这两个方法。(这也是构造函数模式的缺点)
创建特权方法二:静态私有变量
==通过在私有作用域中定义私有变量或函数,同样可以创建特权方法==
1 |
(function(){ |
精髓:变量name是一个静态的,由所有的实例共享的属性
也就是说,在一个实例上调用setName()会影响所有实例。而调用setName()或新建一个Person实例都会赋予name属性一个新值。




近期评论