首先先说立即执行函数,从名字就可以看出来立即执行函数的特点,在JavaScript中用function表示函数声明,如果在函数声明后面加上括号调用是不允许的
function f() {}(); //SyntaxError: Unexpected token )
这里会报错,为了避免错误,我们在函数声明前面添加标识符让引擎认为这是一个表达式,所以下面写法都是正确的。
注意:这里因为立即执行函数是表达式,所以“;”不要忘记省略了。
(function() {})(); (function() {}()); ~function() {}(); !function() {}(); +function() {}(); -function() {}(); //...
不过最常用的还是上面两种,立即执行函数有什么用呢?
- 第一点可以避免污染变量名,立即执行函数不用给函数名起名称。
- 第二形成单独作用域,让外部无法访问到。
什么是闭包?
正常一点的:能让函数记住并能访问所在的作用域称为闭包。
文艺一些的:闭包就是函数内部与函数外部搭建起来的一座桥梁。
闭包的特点: 从定义可以看到,闭包会记住所在的环境,也就是说闭包会一直存在内存之中,所以不应该滥用闭包,在调用完成后手动释放他。
下面是一个例子
function foo() { function fn(name) { return name; } return fn; } var a = foo(); a();
这里是一个闭包,在我们调用a()结束后,因为foo内部的fn是a的引用,所以这个闭包会一直存在。
为什么要使用闭包? 事实上,我们在很多情况下不知不觉的都在使用闭包,就拿作用域来说,函数作用域在外部是无法正常访问到的,但是可以通过在内部定义一个函数返回,在外部拿到想要的变量。
扫描二维码关注公众号,回复:
3052209 查看本文章
function foo() { var a = 10; function fn() { console.log(a); } return fn; } var a = 20; var as = foo(); as(); //10
上面就是一个闭包使用的例子,还有在循环中
for (var i = 0; i < 5; i++) { setTimeout(() => { console.log(i); }, i * 1000); }
猜猜看这里输出的是什么? 答案是5,因为全局环境下就只有一个i,在循环结束后i变成了5,setTimeout是异步调用,所以这里输出的全部是5。
如果想让他正确输出应该怎么做?
这里实现方式有多种,但是这里讨论的是闭包实现,可以在循环内部,嵌套一个立即执行函数。
for (var i = 0; i < 5; i++) { (function (name) { setTimeout(() => { console.log(name); }, i * 1000); })(i); }
这里每次循环都会创建一个新的作用域,因为立即执行函数也是函数可以传递参数给他。
闭包的应用 闭包主要的用途有两种,
- 第一种作为获取函数返回值使用,在上面已经举例说明了;
- 第二种就是作为模块来使用,暴露最少的接口。
下面是一个应用的例子
var fn = function() { var a = {}; function add(name, fn) { a[name] = fn; } function get(name) { return a[name](); } return { add: add, get: get } }; var a = fn(); a.add('fn', function() { console.log('hi'); }); a.get('fn');