function test1() { var a = 1; return function(){ console.log(a); } } test1()(); //1
在test1函数中,返回的匿名函数中a是可以获取到的,利用了闭包的特性。
function test2(cb) { var a = 1; return cb } test2(function(){ console.log(a); })(); // a is not defined
但改写成test2函数,利用回调函数的方式就显示a没有定义,这种时候只要把a传参传入到cb中去就可以得到a。
function test3(cb) { var a = 1; return cb(a) } test3(function(a){ console.log(a); }) // 1
不过这样就不是利用闭包的特性了,想问怎么写才能在回调函数中怎么利用到闭包特性。
function test3(cb) { var a = 1; return cb() } function test4(){ var a = 2; test3(function(){ console.log(a); }); } test4() //2
额,好像写着写着自己有思路了。写成test4函数这样就ok了。
原因是出在匿名函数创建时的上下文环境问题吧,在作为参数的时候已经被创建好了而不是在test3函数内创建的。
参数a因此是2而不是1。
我这样解释正确嘛?
题主已经站在真相的门口了。
先说结论,作用域在函数
被创建
的那一刻起就确定了,而不是调用
。设想一下这样的场景,我们写了一个全局功能函数Foo,如果
a
,b
,c
不是在函数创建的时候确定,那么这个函数基本就没有用了。因为调用者根本不知道a
,b
,c
是什么,甚至不知道他们的存在。正确!!!
作用域的的包含关系是看你函数定义的位置。
函数在调用的时候都会生成一级新的作用域。函数声明和变量创建都是在这个作用域中新定义的。这个新的作用域的上一级作用域是就是看这个函数是在那个作用域里定义的。
比如上面的代码,初始情况下,代码只定义了函数
func1
func3
,他们是在全局定义的。当执行函数func3
的时候,会生成一个新的作用域,由于func3
是在全局定义的,所以这个新的作用域里面定义的的上一级作用域就是全局作用域,在这个作用域中定义了一个变量c
。当再次执行函数func3
的时候,又会生成一个新的作用域,他的上一级还是全局作用域,并且又会在新的作用域创建变量c
(和上一个c
只是重名而已)。同样,第一次调用func1
执行的时候会生成一个作用域(假设叫做func1-1
),在这个作用域上定义了一个函数和变量a
。当执行f21
的时候也会生成一个新的作用域,由于f21
是在func1-1
中定义的,所以其上一级作用域就是func1-1
。当第二次执行func1
的时候,又会生成一个新的作用域(func1-2
),其上有定义了一个func2
和a
(与第一次生成的无关,只是重名,所以说闭包浪费内存)。然后执行,f22
的之后形成一个新的作用域,其上一级是func1-2
。上面的逻辑仅仅在代码上来看就是:作用域的包含关系,主要看函数定义所在的位置,而不是函数调用的位置,所以如果查找一个函数中的变量查找顺序主要是看
{}
(特指函数定义中的{},ES5.1里不存在块作用域)。首先看,本{} 中有没有这个变量定义,没有的话就去找上一级的{}中有没有。。。。一直找到全部代码(全局)中有没有定义。仅仅以这个逻辑就可以解释你上面四段代码。再复杂一点儿的可能就需要上面那一段的逻辑了。首先闭包不是匿名函数,闭包:在函数内声明的变量能够被保存到函数体内,所以换个角度说任何函数都是闭包!
var a="his";
function showa(){
var a="your";
function showa_1(){
var a="my";
function showcb(cb){ //函数作用域:在函数内声明的变量在函数体及其嵌套的函数体的任何地方都是有定义的。
var a=1;
function Obj(){
this.fn=function(){
return cb(a); //传参,尝试去掉a!
}
};
var newA=new Obj();
return newA.fn()
};
return showcb(function(){
if(arguments[0]){
return arguments[0]
}else{
return a;
}
})//这个匿名函数在声明的时候,作用域链就已经确定,它没有形参,不过可以写个判断,依次注释"my","your","his",看结果。
}
return showa_1();
};
showa();