扫码关注官方订阅号
拥有18年软件开发和IT教学经验。曾任多家上市公司技术总监、架构师、项目经理、高级软件工程师等职务。 网络人气名人讲师,...
闭包在函数声明的时候生成,它确定的是变量的查找路径而不是变量的值;变量的值在函数执行时才能确定
var obj = new Object(); var events = {m1: 'clicked', m2: 'changed'}; for (var e in events) { (function() { var aValue = e;//A obj[e] = function() {//B //var aValue = e;//A1 console.log(events[aValue]);//C }; }());//D }; console.log(obj.m1 === obj.m2); //E false obj.m1(); //clicked obj.m2(); //changed
D行定义了一个立即执行函数,函数在执行时引用了当前循环的e值并赋值给立即执行函数的局部变量aValue,B行定义了匿名函数,并且为obj对象添加了e表示的字符串值为属性名的一个属性,并且绑定到匿名函数。匿名函数使用到了D行定义的立即执行函数的局部变量aValue,立即执行函数完毕后,B行匿名函数有对立即执行函数的闭包,确定匿名函数中需要的变量aValue到立即函数中查找
e
aValue
下一次循环时,e变量指向的值发生变化,但先前生成的B匿名函数引用aValue变量值是不会跟着变化的,aValue变量值是确定的,在立即执行函数中。每次循环B行声明的匿名函数对象都是不同的,立即执行函数对象也是不同的
因为有变量应用关系的存在,匿名立即执行函数不会被释放掉,其局部变量也会得到保留~~~
var obj = new Object(); var events = {m1: 'clicked', m2: 'changed'}; for (var e in events) { (function() { //var aValue = e;//A obj[e] = function() {//B var aValue = e;//A1 console.log(events[aValue]);//C }; }());//D }; console.log(obj.m1 === obj.m2); //false obj.m1(); //changed obj.m2(); //changed
和上面不同的地方将A行移动到A1行,看着似乎差不多,但是结果却不同原因就在于B处声明的匿名函数,引用了最外层的变量e,构成对对外层函数的闭包;只是确立了变量引用了关系(确切的说确定了变量查找路径),但是具体的值在函数被执行前是无法预知的等整个循环结束后, e的值为m2(这个也不一定,看events属性定义的顺序及属性值的获取手段),当obj属性绑定的函数被执行时,A1行aValue都为m2~~~~
画了个图:展示了两种情况下,执行到console.log(events[aValue])时的作用域(链)状态。只标出了与aValue查找有关的变量即取值。
console.log(events[aValue])
这样就明白了:第一种情况下,aValue分别取的是外部的“立即执行函数”形成的两个不同闭包中的aValue,两者值不同(因为它们的值是在两次循环期间确定的)。第二种情况下,aValue都取的是Global中的e的值,它的值在循环完成后,变成了'changed'。
'changed'
因为闭包额 兄弟
例2中,你在执行m1和m2的时候,e已经迭代到了最后一个,所以输出都是最后一个,例1中,通过闭包保存了e的值,所以输出是对应的值
答案写在注释中
var obj = new Object(); var events = {m1: 'clicked', m2: 'changed'}; for (var e in events) { (function() { var aValue = e; obj[e] = function() { //这里的aValue是在函数体外赋值的,并不受方法自身影响,同时由于闭包的原因,aValue在赋值后并不会被释放,所以此处的aValue是在for.in 循环时被赋的值 console.log(events[aValue]); }; }()); }; console.log(obj.m1 === obj.m2); //false obj.m1(); //clicked obj.m2(); //changed
var obj = new Object(); var events = {m1: 'clicked', m2: 'changed'}; for (var e in events) { (function() { obj[e] = function() { var aValue = e; //这里的aValue是在方法运行时在函数体内部被赋值的,其值=e,而e是在for.in循环时赋值的,循环结束后e=m2 , 所以在后面执行的过程中,每次会创建一个新aValue变量,变量值为e,e=m2, 所以两次都是changed console.log(events[aValue]); }; }()); }; console.log(obj.m1 === obj.m2); //false obj.m1(); //changed obj.m2(); //changed
关键是aValue在那里定义
var obj = new Object(); var events = {m1: 'clicked', m2: 'changed'}; var aValue;//如果aValue放在这里定义,结果才会是一样的,关键是aValue在哪里定义,而不是 e 的引用是哪一个; for (var e in events) { (function() { aValue = e; obj[e] = function() { console.log(events[aValue]); }; }()); }; console.log(obj.m1 === obj.m2); //false obj.m1(); //changed obj.m2(); //changed
例1的赋值在函数外,执行函数之前aValue的具体值就已经确定;例2的赋值在函数内,执行函数时取aValue的值;这就是最根本的原因,js 函数表达式 延迟解析的机制。
1.作用域链在函数创建时便存在,作用域链是为函数执行时函数所需变量(变量对象)的有序访问而存在的指针;2.作用域链一般不会发生变化(不delate),变量对象内的变量值会随时发生变化;3.闭包会保留父级函数的变量对象,而父级函数再执行完毕会销毁作用域链和执行环境;4.循环内的父级函数每次运行都会创建一次变量对象,并被闭包保存,换句话讲,闭包每次访问的父级变量对象均不相同;5.因此,第一个的情况满足4,既每次循环时,avalue的值都不一样,闭包内的父级函数的变量对象均不相同,所引用的数值也不一样;第二个是因为在循环结束时,e是循环时的变量,只能读到结束时e的数值,所以最后都一样;
如图,(function(){ // some code ...})()这个自执行的匿名函数与上一层的父函数形成链接,当执行m1时,会将值传递给该函数。这就是闭包中子函数可以获得父函数变量的特征。(另一个特征是闭包调用的变量一直在内存中)
当执行obj[e] = function() {} 此时如果是例一:aValue = e;放在该函数前,仍然由于闭包(function(){ // some code ...})()的特性,每次为aValue赋值时,都会由于作用域链,其值会发生改变;此时如果是例二:aValue = e;放在该函数内,该函数由于作用域链,其obj[e]的值e发生变化,但是关联的匿名函数中的变量仅获取变量,其值会按照for执行的最后结果返回给该函数。
如果简化函数:
var obj = new Object(); var events = {m1: 'clicked', m2: 'changed'}; for (var e in events) { (function() { // console.log(e); obj[e] = (function() { console.log(e); })(e); }()); }; obj.m1(); // m1 obj.m2(); // m2
如果将第一层的匿名函数的e值传递给第二次的匿名函数,其值会同时变化。
微信扫码关注PHP中文网服务号
QQ扫码加入技术交流群
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
PHP学习
技术支持
返回顶部
闭包在函数声明的时候生成,它确定的是变量的查找路径而不是变量的值;变量的值在函数执行时才能确定
D行定义了一个立即执行函数,函数在执行时引用了当前循环的
e值并赋值给立即执行函数的局部变量aValue,B行定义了匿名函数,并且为obj对象添加了e表示的字符串值为属性名的一个属性,并且绑定到匿名函数。匿名函数使用到了D行定义的立即执行函数的局部变量aValue,立即执行函数完毕后,B行匿名函数有对立即执行函数的闭包,确定匿名函数中需要的变量aValue到立即函数中查找下一次循环时,
e变量指向的值发生变化,但先前生成的B匿名函数引用aValue变量值是不会跟着变化的,aValue变量值是确定的,在立即执行函数中。每次循环B行声明的匿名函数对象都是不同的,立即执行函数对象也是不同的
因为有变量应用关系的存在,匿名立即执行函数不会被释放掉,其局部变量也会得到保留~~~
和上面不同的地方将A行移动到A1行,看着似乎差不多,但是结果却不同
原因就在于B处声明的匿名函数,引用了最外层的变量e,构成对对外层函数的闭包;只是确立了变量引用了关系(确切的说确定了变量查找路径),但是具体的值在函数被执行前是无法预知的
等整个循环结束后,
e的值为m2(这个也不一定,看events属性定义的顺序及属性值的获取手段),当obj属性绑定的函数被执行时,A1行aValue都为m2~~~~画了个图:展示了两种情况下,执行到
console.log(events[aValue])时的作用域(链)状态。只标出了与aValue查找有关的变量即取值。这样就明白了:第一种情况下,
aValue分别取的是外部的“立即执行函数”形成的两个不同闭包中的aValue,两者值不同(因为它们的值是在两次循环期间确定的)。第二种情况下,aValue都取的是Global中的e的值,它的值在循环完成后,变成了'changed'。因为闭包额 兄弟
例2中,你在执行m1和m2的时候,e已经迭代到了最后一个,所以输出都是最后一个,
例1中,通过闭包保存了e的值,所以输出是对应的值
答案写在注释中
关键是aValue在那里定义
例1的赋值在函数外,执行函数之前aValue的具体值就已经确定;
例2的赋值在函数内,执行函数时取aValue的值;
这就是最根本的原因,js 函数表达式 延迟解析的机制。
1.作用域链在函数创建时便存在,作用域链是为函数执行时函数所需变量(变量对象)的有序访问而存在的指针;
2.作用域链一般不会发生变化(不delate),变量对象内的变量值会随时发生变化;
3.闭包会保留父级函数的变量对象,而父级函数再执行完毕会销毁作用域链和执行环境;
4.循环内的父级函数每次运行都会创建一次变量对象,并被闭包保存,换句话讲,闭包每次访问的父级变量对象均不相同;
5.因此,第一个的情况满足4,既每次循环时,avalue的值都不一样,闭包内的父级函数的变量对象均不相同,所引用的数值也不一样;第二个是因为在循环结束时,e是循环时的变量,只能读到结束时e的数值,所以最后都一样;
如图,(function(){ // some code ...})()这个自执行的匿名函数与上一层的父函数形成链接,当执行m1时,会将值传递给该函数。这就是闭包中子函数可以获得父函数变量的特征。(另一个特征是闭包调用的变量一直在内存中)
当执行obj[e] = function() {}
此时如果是例一:aValue = e;放在该函数前,仍然由于闭包(function(){ // some code ...})()的特性,每次为aValue赋值时,都会由于作用域链,其值会发生改变;
此时如果是例二:aValue = e;放在该函数内,该函数由于作用域链,其obj[e]的值e发生变化,但是关联的匿名函数中的变量仅获取变量,其值会按照for执行的最后结果返回给该函数。
如果简化函数:
如果将第一层的匿名函数的e值传递给第二次的匿名函数,其值会同时变化。
