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声明的变量。
由于全局变量会造成变量污染,高度耦合性等缺点,所以应避免使用全局变量。