第 165 题:闭包的使用场景,使用闭包需要注意什么?
闭包(closure)是 JavasSript 的一大难点,也是它的特色。很多高级应用都要依靠闭包来实现。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
闭包(closure)是 JavasSript 的一大难点,也是它的特色。很多高级应用都要依靠闭包来实现。
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(1)
闭包
什么是闭包
闭包很简单,就是能够访问另一个函数作用域变量的函数,更简单的说,闭包就是函数,只不过是声明在其它函数内部而已。
例如:
myfunc
就是闭包,myfunc
是执行getOuter
时创建的getCount
函数实例的引用。getCount
函数实例维护了一个对它的词法环境的引用,所以闭包就是函数+词法环境当
myfunc
函数被调用时,变量count
依然是可用的,也可以更新的add
接受一个参数x
,返回一个函数,它的参数是y
,返回x+y
add
是一个函数工厂,传入一个参数,就可以创建一个参数和其他参数求值的函数。addFun1
和addFun2
都是闭包。他们使用相同的函数定义,但词法环境不同,addFun1
中x
是4
,后者是5
即:
所以,闭包可以:
使用闭包应该注意什么
代码难以维护: 闭包内部是可以访问上级作用域,改变上级作用域的私有变量,我们使用的使用一定要小心,不要随便改变上级作用域私有变量的值
使用闭包的注意点: 由于闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏。解决方法是,在退出函数之前,将不使用的局部变量全部删除(引用设置为
null
,这样就解除了对这个变量的引用,其引用计数也会减少,从而确保其内存可以在适当的时机回收)内存泄漏: 程序的运行需要内存。对于持续运行的服务进程,必须及时释放不再用到的内存,否则占用越来越高,轻则影响系统性能,重则导致进程崩溃。不再用到的内存,没有及时释放,就叫做内存泄漏
this指向: 闭包的this指向的是window
应用场景
闭包通常用来创建内部变量,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。例如
setTimeout
传参、回调、IIFE、函数防抖、节流、柯里化、模块化等等setTimeout
传参回调
大部分我们所写的 JavaScript 代码都是基于事件的 — 定义某种行为,然后将其添加到用户触发的事件之上(比如点击或者按键)。我们的代码通常作为回调:为响应事件而执行的函数。
例如,我们想在页面上添加一些可以调整字号的按钮。可以采用css,也可以使用:
IIFE
函数防抖、节流
debounce
与throttle
是开发中常用的高阶函数,作用都是为了防止函数被高频调用,换句话说就是,用来控制某个函数在一定时间内执行多少次。使用场景
比如绑定响应鼠标移动、窗口大小调整、滚屏等事件时,绑定的函数触发的频率会很频繁。若稍处理函数微复杂,需要较多的运算执行时间和资源,往往会出现延迟,甚至导致假死或者卡顿感。为了优化性能,这时就很有必要使用
debounce
或throttle
了。debounce 与 throttle 区别
防抖 (debounce) :多次触发,只在最后一次触发时,执行目标函数。
节流(throttle):限制目标函数调用的频率,比如:1s内不能调用2次。
源码实现
debounce
throttle
柯里化
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。
这里定义了一个
add
函数,它接受一个参数并返回一个新的函数。调用add
之后,返回的函数就通过闭包的方式记住了add
的第一个参数。所以说bind
本身也是闭包的一种使用场景。柯里化是将
f(a,b,c)
可以被以f(a)(b)(c)
的形式被调用的转化。JavaScript 实现版本通常保留函数被正常调用和在参数数量不够的情况下返回偏函数这两个特性。模块化
模块化的目的在于将一个程序按照其功能做拆分,分成相互独立的模块,以便于每个模块只包含与其功能相关的内容,模块之间通过接口调用。
模块化开发和闭包息息相关,通过模块模式需要具备两个必要条件可以看出:
常见错误
在循环中创建闭包
这里的
i
是全局下的i
,共用一个作用域,当函数被执行的时候这时的i=3
,导致输出的结构都是3方案一:闭包
方案二:let
如果不想使用过多的闭包,你可以用 ES6 引入的 let 关键词:
方案三:forEach
如果是数组的遍历操作(如下例中的
arr
),还有一个可选方案是使用 forEach()来遍历:原文