函数声明
函数声明时必须有函数名
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 为每次迭代创建了新的作用域,这给了超时回调函数一个机会在每次迭代时闭包一个新的作用域。