2019独角兽企业重金招聘Python工程师标准>>>
有一定JavaScript开发经验的人应该会熟悉下面这种立即执行函数的写法:
(function(name){
console.log('hello ' + name);// hello Bob
})('Bob');
不过即使不熟悉也没关系,这里我会讲解这种写法的含义。
先来看下面这个更容易理解的示例:
var sayHello = function(name){
console.log('hello ' + name);
}
var name = 'Bob';
sayHello(name);// hello Bob
首先使用函数表达式的语法定义函数sayHello
,然后声明一个变量name
,紧接着调用sayHello
,参数为name
。
由于变量name
没有在其他地方被使用,这里其实也就没有必要声明,可以使用更简便的语法,直接把值传递给函数:
sayHello('Bob');// hello Bob
按照这种逻辑,假如sayHello
也是只执行一次,sayHello(name)
不是也可以写成下面这种形式吗?
function(name){
console.log('hello ' + name)
}('Bob');
//Uncaught SyntaxError: Unexpected token (
但是运行发现报错了,这是因为JavaScript将function
关键字当作一个函数声明的开始,而函数声明的后面是不能跟圆括号的。不过,函数表达式的后面可以跟圆括号,要将函数声明转换成函数表达式,只要像下面这样给它加上一对圆括号即可:
(function(name){
console.log('hello ' + name);// hello Bob
})('Bob');
也就是本文开头的立即执行函数,模仿块级作用域正是通过立即执行函数实现的。
来看下面的示例:
function outputNumbers(count){
for(var i = 0; i < count; ++i){
console.log(i);
}
console.log(i);// 5
}
outputNumbers(5);
在Java、C++等语言中,变量i
只会在for
循环的语句块中有定义,循环一旦结束,变量i
就会被销毁。可是在JavaScript中,作用域只分为全局作用域和函数作用域,函数内部声明的变量是绑定在函数作用域对应的变量对象上的,也就是说这里的i
是定义在函数的变量对象上的,在函数内部的任何位置都可以访问它,即使for
循环结束,i
依然存在。
但是如果用立即执行函数包裹for
循环,情况就不一样了:
function outputNumbers(count){
(function(){
for(var i = 0; i < count; ++i){
console.log(i);
}
})();
console.log(i);//Uncaught ReferenceError: i is not defined
}
outputNumbers(5);
此时i
绑定在内部立即执行函数的变量对象上,立即执行函数执行完毕就会销毁,i
也会随之销毁。根据闭包机制,内部的立即执行函数也可以访问到count
。这样就实现了块级作用域——无论在立即执行函数中声明什么变量都不会影响外部变量的使用。
这种技术也经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数,避免冲突,例如:
(function(){
var now = new Date();
if(now.getMonth() == 0 && now.getDate() == 1){
console.log('Happy new year!');
}
})();
这段代码放在任何全局作用域中都可以在1月1日向用户显示祝贺新年的消息,不必担心全局作用域中是否定义了now
变量,从而引起变量的覆盖等问题。