函数声明
函数声明时必须有函数名
function fn(){};
函数表达式
函数表达式中的函数可以为匿名函数,也可以有函数名,但是该函数不能直接使用,只能通过表达式左边的变量 fn 来调用
var fn = function(){};
看看两者区别
function a(){
console.log("函数声明");
}
var b = function(){
console.log("函数表达式");
}
a(); //函数申明
b(); //函数表达式
a(); //函数声明
b(); //报错
function a(){
console.log("函数声明");
}
var b = function(){
console.log("函数表达式");
}
为什么会有这样的结果?
原因: function a(){} 为函数声明,程序运行前就已经存在;var b = function(){} 为函数表达式,属于按顺序执行,所以 b() 会报错
进入IIFE (立即执行的函数表达式)
在ES5中,是没有块级作用域的概念的;我们主要通过匿名函数的方式来块级作用域。
用作块级作用域(私有作用域)的匿名函数的语法:
(function() { //此处是块级(私有)作用域 })();
!function () { //此处是块级(私有)作用域 } ();
~function () { //此处是块级(私有)作用域 } ();
-function () { //此处是块级(私有)作用域 } ();
+function () { //此处是块级(私有)作用域 } ();
//这些都是立即执行的函数表达式的写法
//定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。
IIFE 写法的产生:
var a = function() { console.log("IIFE 写法的产生"); };
a(); //IIFE 写法的产生
//我们将一个匿名函数赋值给了一个全局变量a,然后调用了这个函数
衍生出 IIFE 写法
(function(){
console.log("这是一个立即执行的函数");
})();
//第一个圆括号:将匿名函数转换为函数表达式
//第二个圆括号:立即执行匿名函数(当然,你也可以设置一个函数名)
总结: 1. 创建块级(私有)作用域,避免了向全局作用域中添加变量和函数,因此也避免了多人开发中全局变量和函数的命名冲突
2.IIFE中定义的任何变量和函数,都会在执行结束时被销毁。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链
常用实例
预期: 使用 setTimeout 循环输出 0 1 2 3 4 5
for(var i = 0; i <= 5; i++){
setTimeout(function timer(){
console.log(i);
}, i*1000);
}
//结果:1秒内输出6个6
原因: 超时的回调函数都将在循环完成之后立即运行。
解决方法:
方法一:
for(var i = 0; i <= 5; i++){
(function(){
var j = i;
setTimeout(function timer(){
console.log(j);
}, j*1000)
})();
}
//结果: 0 1 2 3 4 5
方法二:
for(var i = 0; i <= 5; i++){
(function(j){
setTimeout(function timer(){
console.log(j);
}, j*1000)
})(i);
}
//结果: 0 1 2 3 4 5
IIFE 为每次迭代创建了新的作用域,这给了超时回调函数一个机会在每次迭代时闭包一个新的作用域。