作用域
无论如何,函数是唯一拥有自身作用域的结构,其它任何形式的结构都不支持作用域。代码如下所示
var demo = function () { ... };
或
function demo () { ... };
JavaScript语言中没有显式命名空间定义,这意味着所有对象都定义在一个全局共享的命名空间下
引用变量时,向上依次遍历当前作用域、全局作用域,直到发现该变量
隐式全局变量与局部变量
在函数的自身作用域内,如变量声明不使用var表达式,就会导致隐式全局变量产生。先看如下代码所示
var demo = function () { number = 1; }; demo();
或
var demo = function () { for (number = 0; number < 5; number ++) { ... }; }; demo();
运行结果都为声明了一个全局变量number;如不想声明或者覆盖一个全局作用域内的同名变量,就必须使用var表达式。代码如下所示
var demo = function () { var number = 1; }; demo();
或
var demo = function () { for (var number = 0; number < 5; number ++) { ... }; }; demo();
运行结果都为声明了一个仅在当前作用域内才有效的局部变量number
变量声明提升(Hoisting)
变量声明在JavaScript语言中会被提升,这意味着var表达式、function声明会被提升至当前作用域顶部。先看如下代码所示
demo(); var demo = function () { ... };
或
demo(); function demo () { ... };
代码执行之前被转化为
var demo; demo(); demo = function () { ... };
或
function demo () { ... }; demo();
运行结果为第一段代码执行失败,第二段代码执行成功;因为第一段代码执行时demo仅仅是声明了,但是依然缺省值为undefined
非常有意思的是局部变量声明的提升。再看如下代码所示
var number = 0; var demo = function () { if (true) { number = 1; } else { var number = 2; }; console.log(number); }; demo(); console.log(number);
代码执行之前被转化为
var number, demo; number = 0; demo = function () { var number; if (true) { number = 1; } else { number = 2; }; console.log(number); }; demo(); console.log(number);
输出结果为1、0;本该覆盖全局变量number的代码转为声明了一个局部变量number,如想覆盖全局变量number,就必须取消所有的var表达式。代码如下所示
var number = 0; var demo = function () { if (true) { number = 1; } else { number = 2; }; console.log(number); }; demo(); console.log(number);
输出结果为1、1
命名空间
只有一个全局作用域会导致命名冲突,解决方案是匿名包装器,作用是通过匿名函数创建一个新的命名空间。先看如下代码所示
(function (){ ... })();
由于匿名函数被认为是表达式,为了可调用性,需要先行执行,我们可以把自执行的匿名函数理解为匿名包装器。再看如下代码所示
( //小括号内函数先行执行 function () { ... } ) //返回函数对象 (); //立即执行匿名函数,并调用以上执行结果即函数对象
通过匿名包装器创建命名空间,不仅可以防止命名冲突,而且有利于程序的模块化
还有一些其他的函数表达式调用方法。代码如下所示
+function (){ ... }(); !function (){ ... }(); (function (){ ... }());
有意思的思考题
var number = 0; (function (){ console.log(number); //猜猜输出是什么 var number = 1; })();