JavaScript作用域以及闭包(一)
第二篇链接 JavaScript作用域以及闭包(二)
JavaScript作用域
在学习JavaScript的过程中不可避免会遇到闭包问题,其实闭包概念并不难理解,只要理解了JavaScript中作用域概念,那么就很容易理解什么是闭包了。
JavaScript没有块级作用域
//没有块级作用域
if(true){
var sm = "hello";
}
console.log(sm); //hello
JavaScript的作用域是由函数"决定"的,JavaScript的执行环境只有两种——全局和局部(函数)。
作用域链,在JavaScript中标识符的解析是沿着作用域一级级地搜索标识符的过程。搜索过程始终以作用域链的前端开始,然后逐级向后回溯,直到找到标识符(找不到通常会报错)
//作用域链搜索
var name = "json";
(function(){
console.log(name); //undefined
var name = "hello";
})();
这个例子之所以输出的是'undefined'是因为JavaScript的作用域链机制,在执行console语句时,首先会搜索匿名函数内部的作用域,而且恰好找到了name("hello")这个变量,所以外层的name变量被屏蔽了。但执行到console语句时,name还没被定义(或者说是初始化),所以得到的是undefined值。
闭包
那么明白了JavaScript的作用域概念后,所谓的闭包也就不难理解了。通俗地讲JavaScript中每个函数都是闭包(上下文环境:全局),但通常嵌套的函数更能体现闭包特性。下面简单说明下:
按JavaScript作用域链的机制,被嵌套在里面的函数(简称:里函数)是可以访问到其外部的函数(外函数)的变量的。这个很容易理解。而闭包就是,在返回里函数的时候,不仅会返回函数本身还会把函数的上下文环境一并返回。这就是闭包的本质。
//引用自'JavaScript语言精粹'
var quo = function(status){
return {
get_status:function(){
return status;
}
};
};
var myQuo = quo("amazed");
console.log(myQuo.get_status()); //amazed
"即使quo已经返回了,但get_status方法依然享有访问quo对象status属性的特权。get_status方法并不是访问该参数的一个副本,它访问的就是该参数本身。这是可能的,因为该函数可以访问它被创建时所处的上下文环境。这被称为闭包"
下面这段代码比上面那段更为通俗,通过下面的例子对闭包可以有进一步的理解:
//引用自'nodejs开发指南'
var smClosure = function(){
var count = 0;
var get = function(){
count++;
return count;
};
return get;
}
var sm1 = smClosure();
var sm2 = smClosure();
console.log(sm1()); //1
console.log(sm2()); //1
console.log(sm1()); //2
console.log(sm1()); //3
console.log(sm2()); //2
sm1,sm2分别调用了smClosure()函数,生成了两个闭包实例,他们内部引用的count分别属于各自的执行环境。这就是所谓的返回内部函数时,不仅会返回函数本身还会一并返回其上下文环境的意思。
扩展:
关于作用域,JavaScript的变量和函数声明都会被存储到执行上下文的变量对象中,即声明提升。函数声明的优先级高于变量声明的优先级,但不会覆盖变量赋值。
而命名函数表达式的标识符(即函数名)在外部作用域是无效的,只在函数作用域内有效。
var sm = function something(){
console.log(typeof something); //function
}
sm();
console.log(typeof something); //undefined
console.log(typeof sm); //function
参考
《JavaScript高级程序设计》
《JavaScript语言精粹》
《nodejs开发指南》