定义函数表达式的方式有两种:函数声明,函数表达式
两者之间的区别是:函数声明有一个重要的特性函数声明提升
在执行代码之前会先读取函数声明。意味着可以把函数声明放在调用它的语句后面。
1.1 递归
递归函数是在一个函数通过名字调用自身的情况下构成的
arguments.callee 是一个指向正在执行的函数的指针,可以用它来实现对函数的递归调用( 非严格模式下可以使用)
1.2 闭包
指有权访问另一个函数作用域中的变量的函数
区分匿名函数和闭包的概念
创建闭包的常用方式:在一个函数内部创建另一个函数,内部函数可以访问外部函数中的变量。因为内部函数的作用域链中包含外部函数的作用域
理解闭包之前,了解作用域链的概念至关重要(如何创建作用域链,作用域链的作用)
当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链。然后,使用 arguments 和其他命名参数的值来初始化函数的活动对象
在函数执行过程中,读取和写入变量的值,首先需要在作用域链中查找变量。
function compare(value1, value2){
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);
![](https://i-blog.csdnimg.cn/blog_migrate/26f349d3bd5213bf8d124192ce15b7ae.png)
显然,作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
一般来讲,当函数执行完毕,局部活动变量会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象),但闭包的情况不同
在一个函数A 内部定义的函数B 会将外部函数A 的活动变量添加到它的作用域链中。因此在函数A 内部定义的匿名函数的作用域中,实际上会包含外部函数的活动对象。
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;
}
};
}
var compareNames = createComparisonFunction("name"); //创建函数
var result = compareNames({ name: "Nicholas" }, { name: "Greg" }); //调用函数
compareNames = null; //解除对匿名函数的引用(以便释放内存)
由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,建议只在绝对必要时再考虑使用闭包。
在使用闭包时应注意两点:
2.1 闭包与变量
闭包只能取得包含函数中任何变量的最后一个值。闭包保存的是整个变量对象,不是某个特殊变量
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result; // 想获得0-9 实际获得10个9
}
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
* result[i] = function(num){
* return function(){
* return num;
* };
* }(i);
}
return result; //0-9
}
利用立即执行函数,将其结果赋值给数组。
在调用立即执行函数(匿名函数)时,传入了变量 i。由于函数参数是按值传递的,所以会将变量 i 的当前值复制给参数 num。而在匿名函数内部,创建并返回了一个访问 num 的闭包。这样一来,result 数组中的每个函数都有自己num 变量的一个副本,因此可以返回各自不同的数值
2.2 关于this对象
this 对象是在运行时基于函数的执 行环境绑定的:
在全局函数中,this 等于 window; 当函数被作为某个对象的方法调用时,this 等于那个对象
1、匿名函数的执行环境具有全局性,因此其this对象指向window
2、每个函数在被调用时都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。解决办法:把外部作用域中的this 对象保存在一个闭包能够访问到的变量中,就可以让闭包访问该对象了。
总结:
- 函数表达式不同于函数声明。函数声明要求有名字,但函数表达式不需要。没有名字的函数表达式也叫做匿名函数。
- 在无法确定如何引用函数的情况下,递归函数就会变得比较复杂;
- 递归函数应该始终使用 arguments.callee 来递归地调用自身,不要使用函数名——函数名可
- 能会发生变化。
- 在后台执行环境中,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域。
- 通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。
- 但是,当函数返回了一个闭包时,这个函数的作用域将会一直在内存中保存到闭包不存在为止。
- 创建并立即调用一个函数,这样既可以执行其中的代码,又不会在内存中留下对该函数的引用。
- 结果就是函数内部的所有变量都会被立即销毁——除非将某些变量赋值给了包含作用域(即外部作用域)中的变量。
- 即使 JavaScript 中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量。
- 有权访问私有变量的公有方法叫做特权方法。
- 可以使用构造函数模式、原型模式来实现自定义类型的特权方法,也可以使用模块模式、增强的模块模式来实现单例的特权方法。