js中作用域的思考
和传统的C,C++等语言不同,javascript中的作用域不是以花括号包围的块级作用域(block scope),不妨看看以下代码:
if(true){
var val = "test-val";
}
console.log(val);
在js中运行以上代码,控制台打印输出:“test-val”;而在C语言中,会报错,这是为什么呢?
这是因为:JavaScript 的作用域完全是由函数来决定的。
函数作用域
在一个函数中定义的变量只对这一个函数有效,我们称之为函数作用域。当在函数内使用一个变量时,js首先会搜索函数作用域,又称为局部作用域。如果未找到,则搜索上层作用域,直至全局作用域。
举个栗子:
var name="global";
var fn1 = function (){
console.log(name);
}
var fn2 = function () {
var name="area";
console.log(name);
};
fn1(); //global
fn2(); // area
就像你看到的,js的变量搜索是由内而外,函数作用域优先级最高!所以在执行fn2()时会优先取局部变量的值area。那么接下来的一短代码,可能会让你迷茫:
var name ="global";
var fn = function () {
console.log(name);
var name = "area";
}
fn();// undefined
不要迷茫!这是因为在执行fn()时,在console.log函数访问到变量name时,会优先从函数作用域内进行搜索,而这时恰巧能搜索到变量name,则会屏蔽掉上级的变量name。又加上在执行console.log(name)时,name变量还未被定义,或者我们称之为位初始化,就会输出undefined。
也有一部分人把上述现象称之为:变量提升。所谓变量提升,指的是在刚进入函数时,函数内部的所有变量都被定义了,但直到遇见var的时候,该变量才被初始化。在初始化之前的所有的引用,都会得到undefined。
函数作用域的嵌套
(function () {
var scope ="top";
(function(){
var scope = "middle";
(function () {
console.log(scope);//middle
})()
})()
})()
为什么会输出middle呢?这是因为在访问最内层的变量scope时,会优先搜索函数作用域,结果没搜到,则由内而外搜到其父级作用域middle。
有一点需要注意:函数作用域的嵌套关系由定义时决定,而不是由引用时决定。举个栗子:
var name ="top";
var fn1 = function (){
console.log(name);
}
fn1();//top
var fn2 = function () {
var name="middle";
fn1();
}
fn2();//top
为什么会得到top而不是middle呢?这是因为通过fn2调用fn1()时,访问的变量name不是fn2中的变量,而是其父级作用域的变量。
全局作用域
在js中有一种特殊对象,在node中对应global对象,在浏览器中对应window对象。我们称之为全局对象,又称为全局作用域。它可以在js的整个作用域范围内被使用而不受限制。
满足以下几点可以称为全局对象:
» 在最外层定义的变量
» 全局对象的属性
» 变量的隐式声明,即不通过var声明的变量。
由于全局变量会造成变量污染,高度耦合性等缺点,所以应避免使用全局变量。
浙公网安备 33010602011771号