一. 立即执行函数
1. 概念
声明一个函数,并马上调用这个匿名函数就叫做立即执行函数。
2. 立即执行函数的作用
1. 不必为函数命名,避免了污染全局变量。( 什么叫全局变量污染:就是直接给window添加属性和方法,污染全局变量的后果就是,在多人开发中,容易造成命名的冲突)
2. 立即执行函数内部形成了一个独立的作用域,可以封装一些外部无法读取的私有变量。这个作用域里面的变量,外面访问不到,这样就可以避免变量污染。
3. 闭包和私有数据
//立即执行函数的两种写法
//第一种:用括号把整个函数定义和调用包裹起来
(function(){
//function body
}())
//第二种:用括号把函数定义包裹起来,后面再加括号
(function (){
//function body
})()
通常,看到这样的代码,会觉得这样的执行效果是点击第一个li,则会输出1,点击第二个li,则会输出二,以此类推。
<body>
<ul id="list">
<li>公司简介</li>
<li>联系我们</li>
<li>营销网络</li>
</ul>
<script>
var list = document.getElementById("list");
var li = list.children;
for(var i = 0 ;i<li.length;i++){
li[i].onclick=function(){
alert(i); // 结果总是3.而不是0,1,2
}
}
</script>
</body>
但是真正的执行效果是,不管点击第几个li,都会输出3。
因为 i 是贯穿整个作用域的,而不是给每个 li 分配了一个 i,用户触发的onclick事件之前,for循环已经执行结束了,而for循环执行完的时候i=3。
1. 利用立即执行函数解决
<body>
<ul id="list">
<li>公司简介</li>
<li>联系我们</li>
<li>营销网络</li>
</ul>
</body>
<script>
var list = document.getElementById("list");
var li = list.children;
for (var i = 0; i < li.length; i++) {
(function(j) {
li[j].onclick = function() {
alert(j);
}
})(i); //把实参i赋值给形参j
}
</script>
改变变量i的作用域,把全局变量i以参数的形式传递到立即执行函数中,在立即执行函数中定义变量i的形参变量j,变量j就是在立即执行函数的作用域中。(给每个li创建了一个作用域块,点击的时候寻找自由变量j,在立即执行块中找到)
2. 使用ES6解决
<body>
<ul id="list">
<li>公司简介</li>
<li>联系我们</li>
<li>营销网络</li>
</ul>
<script>
var list = document.getElementById("list");
var li = list.children;
for(let i = 0 ;i<li.length;i++){
li[i].onclick=function(){
alert(i); // 结果是0,1,2
}
}
</script>
</body>
3. 立即执行函数的参数
(function(j){
//代码中可以使用j
})(i)
如果立即执行函数中需要全局变量,全局变量会被作为一个参数传递给立即执行函数(上例中的i就是一个全局变量,i代表的是实参,j是i在立即执行函数中的形参)。
二. 闭包函数
1. 变量的作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
2. 如何从外部读取局部变量?
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
3. 闭包的概念
闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
4. 闭包的用途
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
当看到function函数里面嵌套function函数,就是闭包。
当想访问函数内部的变量时,或想把函数内部的变量抛到外面,就需要用到闭包的写法。
父函数将子函数作为返回值,再将子函数赋值给一个变量。所以子函数会存在于内存中,而子函数依赖于父函数的存在,所以父函数也会存在于内存中。也就不会被垃圾回收机制回收。
5. 闭包的写法(用法)
在主函数function内部,定义闭包function函数。但是执行调用还是在外面的,即通过return才能调用执行。
其中,funOne是外部函数,funTwo是内部函数。funOne()的返回值是函数funTwo,funTwo在funOne()作用域内部,所以它可以获取funOne作用域下变量num的值,并将这个值作为作为返回值赋值给全局作用域下的变量fun。实现了在全局变量下获取到局部变量中的变量的值。调用外部函数funOne并将其赋值给fun时,打印fun,可以看到会返回函数funTwo。
将闭包与匿名函数结合起来,可以这样用:
注意:在我console.log(fun())的结果中,每一次都是在前一次的基础上发生了变化的(从1>2>3),这说明,num的结果每一个都被保留了下来。
而在console.log(funTwo())的结果中,每一次都是一样的(都是1),这说明,里面的num每一次都被销毁重建了。