闭包的基础知识
变量作用域链
- JS的两种变量:全局变量、局部变量
- 局部变量作用域一般指函数内部及{},此外视为全局变量
- JS的链式作用域:理解为函数嵌套,子函数调用父函数变量,会一级一级往上寻找变量,子函数变量对父函数不可见。
闭包概念
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式,在一个函数内部创建另一个函数。之所以一个内部的函数可以访问其外部的变量,而且在其被返回或是调用时还可以访问,是因为这个内部函数的作用域链中包含外部函数的作用域。
闭包解决了什么
闭包可以缓存上级作用域,那么就使得函数外部打破了“函数作用域”的束缚,可以访问函数内部的变量。以平时使用的Ajax成功回调为例,这里其实就是个闭包,由于上述的特性,回调就拥有了整个上级作用域的访问和操作能力,提高了极大的便利。开发者不用去写钩子函数来操作上级函数作用域内部的变量了
闭包使用注意
- 闭包会使得函数中的变量都被保存在内存中不回收,会导致内存爆满,滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
闭包的使用场景
面试被问时 闭包随处可见,一个Ajax请求的成功回调,一个事件绑定的回调方法,一个setTimeout的延时回调,或者一个函数内部返回另一个匿名函数,这些都是闭包。简而言之,无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都有闭包的身影。
闭包使用Demo
几个 li 中 点击当前 li 返回当前的下标
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<Script>
var li = document.querySelectorAll('li');
for(var i=0;i<li.length;i++){
li[i].onclick = function(){
console.log(i)
}
}
</Script>
此时点击返回的都是4,因为i时用var定义的全局变量,点击触发时i已经为4,解决方法如下:
var li = document.querySelectorAll('li');
for(var i=0;i<li.length;i++){
(function(i) {
li[i].onclick = function(){
console.log(i)
}
})(i)
}
在循环时把i传进闭包里面。
函数防抖(debounce):
就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
函数节流(throttle):
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。