1.js支持块作用域吗?
for(var i=0;i<9;i++){
}
alert(i);
运行结果:
2.函数作用域
我们已经知道,在任意代码片段外部添加包装函数,可以将内部的变量和函数定义“隐藏”起来,外部作用域无法访问包装函数内部的任何内容。
例如:
var a=2;
function foo(){
var a=3;
console.log(a);
}
foo();
console.log(a);
虽然这种技术可以解决一些问题,但是它并不理想,因为会导致一些额外的问题。首先,必须声明一个具名函数foo(),意味着foo这个名称本身“污染”了所在的作用域(在这个例子中是全局作用域)。其次,必须显示地通过函数名foo()调用这个函数才能运行其中的代码。
如果函数不需要函数名(或者至少函数名可以不污染所在作用域),并且能够自动运行,这将会更加理想。
JavaScript提供了能够同时解决这两个问题的方案:
(function foo(){
var a=3;
console.log(a);
})();
console.log(a);
接下来分别介绍这里发生的事情
首先,包装函数的声明以(function...而不是function....开始。尽管看上去这并不是一个很显眼的细节,但实际上却是非常重要的区别。函数会被当作函数表达式而不是一个标准的函数声明来处理。
注:区分函数声明和表达式最简单的方法是看function关键字出现在声明中的位置(不仅仅是一行代码,而且是整个声明中的位置)。如果function是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
函数声明和函数表达式之间最重要的区别是它们的名称标识符会绑定在何处。
比较一下前面两个代码片段。第一个片段中foo被绑定在作用域中,可以直接通过foo()来调用它。第二个片段中foo被绑定在函数表达式自身的函数中而不是所在作用域中。
换句话说,(function foo(){...})作为函数表达式意味着foo只能在....所代表的位置中被访问,外部作用域则不行。foo变量名被隐藏在自身中意味着不会污染外部作用域。
3.js词法作用域(也称为静态作用域或闭包)
问:js支持动态作用域吗?
function f1(){
alert(k);
}
function f2(){
var k=3;
f1();
}
f2();
很明显,在f2()中定义的k并没有在f1中成功调用。所以,js不支持动态作用域。
再看下面这个例子:
var x="global value";
var g=function(){
alert(x);
var x="local value";
}
g();
函数外部的x="global value"可以视为全局变量,那么调用函数g(),函数内的alert方法自然会抛出“global value”,没错,正常逻辑下就是这样,然而,结果是:
这不科学啊……
我们来仔细分析一下js的词法作用域原理:首先说下js的预定义机制,js解释器首先会对var定义的变量进行初始化,这个初始化只是定义并没有赋值,也就是函数g中的
var x="local value";预定后的存在方式是var x,在代码执行到var x="local value";之前不会赋值,而恰恰是在该条语句之前调用了alert(x)方法,根据词法作用域规则,变量x首先会在当前函数作用域内寻找,若找不到则到高层作用域(也就是本例中的全局作用域)中寻找,不幸的是,在函数作用域中确实找到了,找到的是没有赋值的x,所以输出undefined。由内到外的作用域链的建立和js特有的预定义机制是理解本例的关键。
我们把代码稍作改动:
var x="global value";
var g=function(){
alert(x);
}
g();
便可得到我们想看到的结果: