JS之模仿块级作用域_立即执行函数
模仿块级作用域-立即执行函数
前言:最近在细读Javascript高级程序设计,对于我而言,中文版,书中很多地方一笔带过,所以用自己所理解的,尝试细致解读下。如有纰漏或错误,会非常感谢您的指出。文中绝大部分内容引用自《JavaScript高级程序设计第三版》。
JavaScript没有块级作用域的概念(ES5中没有)。这意味着在块语句定义的变量,实际上是在函数中而非语句中创建的。
function outputNumbers(count) { for( var i = 0; i < count; i ++ ) { console.log(i); // 0,1,2,3,4,5,6,7,8,9 } console.log(i); //10 } outputNumbers(10);
在函数outputNumbers()中定义了一个for循环, 而变量i的初始值被设置为0。
在Java、C++等语言中,变量i只会在for循环的语句中有定义,循环一旦结束,变量i就会被销毁。
可是在JavaScript中,变量只是定义在函数outputNumbers()的活动对象中的,因此从它有定义开始,就可以在函数内部随处访问它。
即使像下面这样错误地重新声明同一个变量,也不会改变它的值。
function outputNumbers(count) { // 注意变量提升 var i for(var i = 0; i < count; i++) { console.log(i); // 0, 1, 2, 4, 5, 6, 7, 8, 9 }; var i; console.log(i); // 10 } outputNumbers(10);
JavaScript从来不会告诉你是否多次声明了同一个变量;
遇到这种情况,它只会对后续的声明视而不见(不过,它会执行后续声明中的变量初始化)。
匿名函数可以用来模仿块级作用域并避免这个问题(其实就是立即执行函数), 一定要注意变量的生命周期,局部变量只在执行环境中存在,块级作用域其实就是立即执行函数,即刻产生一个作用域,块级作用域里面的变量只在块级作用域里面,避免了变量污染,隔离出一个独立的作用域(私有作用域)。
//语法如下 (function(){ //这里是块级作用域 })()
以上代码定义并立即调用了一个匿名函数。
将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。
而紧随其后的另一对圆括号会立即调用这个函数。
这种语法,确实很不好理解。
来看下下面的例子
var someFunction = function() { //这里是块级作用域 }; someFunction();
这个例子先定义了一个函数,然后立即调用它。定义函数的方式是创建一个匿名函数,并把匿名函数赋值给变量someFunction。而调用函数的方式是在函数名称后面添加一对圆括号,即someFunction()。那么我们可不可以,声明函数和调用函数写在一块呢?
function(){ //这里是块级作用域 }(); //出错
这段代码会导致语法错误,因为JavaScript将function关键字当做一个函数声明的开始。而函数声明后面不能跟圆括号。
如果我们在function关键字前面加一个运算符,这样就不会让JavaScript将function关键字当做函数声明的开始。解析器,就会依次从左往右解析代码。
//解析过程 运算符 => 函数声明function => 调用() !function(){ console.log("hi"); }(); // "hi" +function(){ console.log("hello"); }(); // "hello" -function(){ console.log("World"); }(); // "World" (function(){ console.log("ciao"); })(); // "ciao"
无论在什么地方,只要临时需要一些变量,就可以使用私有作用域。
function outputNumbers(count) { //立即执行函数,隔离出一个独立的作用域,避免变量的污染 //同时也是一个闭包, 能访问到外部函数中的count (function(){ for(var i= 0; i < count; i++) { console.log(i); } })(); console.log(i); // error } outputNumbers(10); // 0,1,2,3,4,5,6,7,8,9 //i is not defined
这种技术(使用私有作用域),经常在全局作用域使用。
**从而限制向全局作用域中添加过多的变量和函数。
因为会产生变量污染,私有作用域可以访问到全局变量,而全局变量访问不到私有作用域里面的变量。**
一般来说,我们都应该尽量少向全局作用域中添加变量和函数。
在一个由很多开发人员共同参与的大型应用程序中,过多的全局变量和函数很容易导致命名冲突。
而通过创建私有作用域,每个开发人员都可以使用自己的变量,又不必担心搞乱全局作用域。
(function(){ var now = new Date(); if(now.getMonth() == 0 && now.getDate() == 1) { console.log("Happy New Year!"); } })();
把上面这段代码放在全局作用域中,可以用来确定哪一天是1月1日。
如果到了这一天,就会向用户显示一条祝贺新年的信息。
其中的变量now现在是匿名函数中的局部变量,而我们不必在全局作用域中创建它。
这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。