就是在执行代码之前会先读取函数声明,也就表示可以把函数声明放在调用语句后面。
两种定义函数的方式:
1)函数声明:
function functionName(arg0, arg1, arg2) {
//函数体
}
2)函数表达式:
var functionName = function(arg0, arg1, arg2){
//函数体
};
第二种方式创建了一个匿名函数,并将其赋给变量functionName,因此,使用前如果未赋值就会出错,就像这样:
sayHi(); //错误:函数还不存在
var sayHi = function(){
alert("Hi!");
};
function fun1(){
alert(‘fun1‘);
}
var fun2 = fun1;
fun1 = null;
fun2(); // "fun1"
正常来说,这段代码不会有问题,但是递归函数中就会出问题。
function factorial(num){ if (num <= 1){ return 1; } else { return num * factorial(num-1); } } var anotherFactorial = factorial; factorial = null; alert(anotherFactorial(4)); //出错!
factorial = null之后不再是函数了,但是原定义中依然使用了factorial(),因此出错。
arguments.callee是一个指向正在执行函数的指针,因此可修改为:
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); } });
3. 作用域链
当某个函数被调用时,会创建一个执行环境及相应的作用域链,以保证对执行环境有权访问的所有变量和函数的有序访问。
然后,使用arguments和其他命名参数的值来初始化函数的活动兑现。
作用域链的前端,是当前执行代码所在环境的变量对象,作用域链的下一个变量对象来自外部环境,如此一直延续到全局执行环境。
例:
function compare(value1, value2){ if (value1 < value2){ return -1; } else if (value1 > value2){ return 1; } else { return 0; } } var result = compare(5, 10);
上面在全局作用域中调用了compare()函数,调用compare()时,会创建一个包含arguments、value1和value2的活动对象。全局执行环境的变量对象(包含result和compare)在compare()执行环境的作用域中处于第二位,如下:
后台的每个执行环境都有一个变量对象(表示变量的对象)。全局环境的变量对象始终存在,而compare()函数这样的局部环境的变量对象,则只存在于函数执行的过程中。
在创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存于内部的[[Scope]]属性中。
在调用compare()函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。
此后,又有一个活动对象被创建并被推入执行环境作用域链的前端。
此例中compare()的执行环境的作用域链中包含两个变量对象:本地活动对象和全局变量对象。
作用域链本质上是一个指向变量对象的指针列表,只引用但不实际包含变量对象。
无论什么时候在函数中访问一个变量,就会从作用域链中搜索具有相应名字的变量。一般来讲,函数执行完毕后,局部活动对象就会被销毁,内存中仅包含全局执行环境的变量对象。但,闭包属于二般情况。
闭包就是有权访问另一个函数作用域中的变量的函数,是个函数。
一般创建闭包,就是在一个函数内部创建另一个函数。这样:
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; } }; }
上面在createComparisonFunction()内部创建了一个匿名函数,匿名函数内部访问了外部函数中的变量propertyName(var value1 = object1[propertyName];)。该匿名函数被返回后无论在哪儿被调用都可以访问变量 propertyName。
下面代码执行时作用域链是这样的:
var compare = createComparisonFunction("name");
var result = compare({ name: "Nicholas" }, { name: "Greg" });
在createComparisonFunction()函数执行完毕后,其执行环境的作用域链会被销毁,但其活动对象会留在内存中不被销毁,因为匿名函数的作用域链仍在引用这个活动对象。直到匿名函数被销毁后,createComparisonFunction()的活动对象才被销毁,例如:
//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除对匿名函数的引用(以便释放内存),就等于通知垃圾回收例程将其清除
compareNames = null;
闭包保存的是整个变量对象,因此闭包只能取得包含函数中任何变量的最后一个值。
function createFunctions(){ var result = new Array(); for (var i=0; i < 10; i++){ result[i] = function(){ return i; }; } return result; }
执行createFunctions()后返回的函数数组,每个的执行结果都是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; }
上面对于数组每项都立即执行了一个匿名函数,在调用每个匿名函数时,传入了变量i,由于参数按值传递,因此变量i的当前值会复制给参数num。而匿名函数内部,又返回了一个访问num的闭包,而result数组中每个函数都有自己num变量的一个副本,因此就可以返回各自索引了。
匿名函数的执行环境具有全局性,因此其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", getName: function(){ return this.name; } }; object.getName(); //"My Object" (object.getName)(); //"My Object" (object.getName = object.getName)(); //"The Window",在非严格模式下
js并没有块级作用域的概念,像这样:
function outputNumbers(count){ for (var i=0; i < count; i++){ alert(i); } alert(i); //计数 }
模仿块级作用域的匿名函数的语法如下:
(function(){
//这里是块级作用域
})();
上面其实是一个函数表达式,函数表达式的后面可以跟圆括号。而函数声明的后面不可以跟圆括号,这样:
function(){
//这里是块级作用域
}(); //出错!
因此,让上面的变量i私有可以这样做:
function outputNumbers(count){ (function () { for (var i=0; i < count; i++){ alert(i); } })(); alert(i); //导致一个错误! }
这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。
原文:http://www.cnblogs.com/sduzhangxin/p/4356013.html