您的位置:首页 > Web前端 > JavaScript

JS闭包

2016-04-15 14:58 141 查看

函数表达式

定义函数的方式有两种,1、函数声明(即正常 function a(){}方式) 2、函数表达式(即匿名函数 var a=function(){})

两者的区别在于函数的提升,也就是说函数声明的方式ECMAScript会优先读取函数声明,因此无论在函数声明的上方还是下方调用函数,都不会出错。

而函数表达式则不行,调用的语句必须放在函数表达式的下方才可。

还有一个使用的不同,如:

if (1 > 0) {

function sayHi() {

alert(1);

}

}

else {

function sayHi() {

alert(2);

}

}

sayHi(); //2


按照正常逻辑函数应该返回为1才对,然后却返回2,这是因为ECMAScript会优先读取函数声明,又因为重复定义了两个相同的函数,第二个函数会自动覆盖第一个函数,所以无论条件如何都会返回2。

用函数表达式则可以正确的得到效果。如:

if (1 > 0) {

var sayHi = function () {

alert(1);

}

}

else {

var sayHi = function () {

alert(2);

}

}

sayHi(); //1


闭包

闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。理解闭包,先从理解正常的函数调用过程开始。如:

在函数的执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量。

function compare(value1, value2) {

if (value1 > value2) {

return -1;

}

else {

return 1;

}

}

Var result=compart(5,10);


上面的代码定义了compare函数,然后在全局作用域中进行调用。当调用时,会创建一个包含arguments,value1和value2的活动对象,全局执行环境的变量对象(包含result和compare)注意全局变量对象在compare的作用链中处于第二位。



全局变量对象始终都会存在,当创建compare函数时,会创建一个预先包含全局变量对象的作用域链,指向全局变量对象。这个作用域链会被保存到当前函数的内部的[scope]中

当调用compare函数时,会为函数创建一个执行环境,然后复制当前函数[scope]中的对象,构建起执行环境的作用域链,然后在将活动对象放到作用域链的最前端。

这样当操作某个变量时,就会从作用域链从前向后的按地址进行搜索变量。当函数执行完成之后,局部活动对象就会被销毁,内存中仅存在全局变量对象。

在一个函数内部定义了另一个匿名函数,(这里按父函数和子函数区分),在子函数中,其作用域链会包含父函数活动对象。(按照作用域链的概念)

这样子函数就可以访问父函数中的所有变量,更为重要的是,父函数执行完毕之后,其活动对象也不会被销毁,因为在子函数的作用域链中还进行引用这个活动对象,所以只有父函数中的作用域链会被销毁。

Function  createComparisonFunction (propertyName) {

return function (object1, object2) {

var value1 = object1[propertyName];

var value2 = object2[propertyName];

if (value1 > value2) {

return -1;

}

else {

return 1;

}

}

}

//返回匿名方法给comparNames

var comparNames = createComparisonFunction ("name");

//实际上调用匿名方法

var result = comparNames({ name: "Yu" }, { name: "Kai" });

//解除匿名函数的引用 销毁闭包的活动对象

comparNames = null;


上述代码,因为comparNames 始终引用createComparisonFunction方法内部返回的匿名方法,所以导致匿名方法的执行环境始终存在,所以也就无法回收其作用域链和活动对象,又因为匿名方法的作用域链会引用其父类的活动对象,导致其父类的活动对象也无法回收,这就导致了闭包。如果解除则将闭包对象的引用设置为null即可。



闭包与变量的问题

闭包只能取得包含函数中的最后一个值,即下面例子中i的最后一个值10。

function createFunctions() {

var result = new Array();

for (var i = 0; i < =10; i++) {

result[i] = function () {

return i;

}

}

return result;

}

var a = createFunctions();

alert(a[2]());


上面的代码按照逻辑应该会返回一个数组,这个数组的值就是元素的索引,但实际内部的值都会是10.这是因为每个函数的作用域链中都存放着createFunctions函数的活动对象,所以他们都是同一个变量i,当createFunctions函数返回后,i的值就是10,所以数组内部的值也都会为10。

也就是说上面的代码其实是赋值了10次,把方法的地址给到了数组中的每个元素,而此时不会执行方法,也并不会返回i的值,而当调用时,方法会返回i的值,又因为闭包的原因createFunctions活动对象并没有回收,所以只会读到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;

}

var a = createFunctions();

alert(a[2]());


上述代码可以理解为:我们没有直接把闭包赋值给数组,而是又定义了一个匿名函数。并将立即执行该匿名方法的值赋值给数组,在调用每个匿名函数时,我们传入了i,由于函数参数是按值传递的,所以就会将变量i的当前值赋值给参数num。而在这个匿名函数的内部又创建了一个返回num的闭包,这样一来,数组中每个函数都会有自己num副本了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: