We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
一个函数和对其周围状态(Lexical Environment,词法环境,详细可看《执行上下文、作用域到底是什么?二者有什么关系》) 的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。
举个例子:
function bar(){ let a = 1 return function foo(){ let b = 1 return a + b } } bar()()//2
在上述的例子中,其作用域伪代码如下:
GlobalEnvironment = { EnvironmentRecord: { // 标识符 }, outer: null }; barEnvironment = { EnvironmentRecord: { a: 1, bar: '<fun>' } outer: GlobalEnvironment }; fooEnvironment = { EnvironmentRecord: { b: 1, } outer: barEnvironment };
因为静态作用域的特点,foo函数会从bar 中获取a的值,但是这里的bar 已经执行完毕,执行上下文已经出栈了,a 变量肯定也不存在了,这就与我们闭包的定义矛盾了。所以这里闭包函数对外层函数的变量必定会特殊处理,以保证闭包函数可以使用,我们可以认为闭包就是将引用的外部函数的变量中记住了,类似于引用变量,存储在了“堆”中。
foo
bar
a
闭包让函数以外也可以访问到函数内部的变量,这样可以用来创建私有变量,比如:
//简单闭包 function demo(){ var a = 1 return function(){ return a } } var t = demo() t()//1
常见的用法就是防抖和节流函数:
// 防抖 function debounce(fn,wait){ let timer let that let arg const delayFn = function(){ const _that = that const _arg = arg that = arg = undefined timer = null return fn.apply(_that,_arg) } return function(){ that = this arg = arguments timer = setTimeout(()=>{ if(timer){ clearTimeout(timer); } timer = setTimeout(delayFn, wait); }) } } // 节流 function throttle(fn,wait){ let timer let that let arg const delayFn = function(){ const _that = that const _arg = arg that = arg = undefined timer = null return fn.apply(_that,_arg) } return function(){ that = this args = arguments if(!timer){ timer = setTimeout(delayFn, wait); } } }
通过匿名立即执行函数,单独开辟一个作用域,使得只暴露相关接口函数,达到模块化。
//仅调用一次的设计模式——单体模式 var module = (function(global){ var text = '1' var fn1 = function (){ // ... console.log(text,1) } var fn2 = function fn2(){ // ... console.log(text,2) } return { fn1: fn1, fn2: fn2 } //也可以直接用暴露到window上 //global.myModule={ // fn1: fn1, // fn2: fn2 // } })(window) module.fn1()//1 1 module.text //undefined // myModule 暴露到window上
下文的代码中,我们期望在延迟函数中输出的是1,2,3,4,5,但实际上却并非如此:
1,2,3,4,5
//异常情况 for(var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i);//6 6 6 6 6 6 }); }
这是由于setTimeout内的函数发现自身i这个变量未定义时,会作用域链去查找i,而这里的i是var声明的,只会声明一次,也就是说后续i++都会修改这个值,最终i变成了6,等到setTimeout函数执行时,输出的自然都是6。 我们可以采用闭包的方式,单独开辟一个作用域给每次循环的setTimeout,这样输出就会跟我们所想的一致了。
setTimeout
i
var
i++
6
//正常情况 for(var i = 1; i <= 5; i++) { (function(j){ //这里是块级作用域 var j = i setTimeout(function timer() { console.log(j);//1 2 3 4 5 6 }); })(i); }
//变量可能发生改变 function test() { let i = 1 var result = function(){ console.info(i) //2 } i = 2 return result }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
定义
一个函数和对其周围状态(Lexical Environment,词法环境,详细可看《执行上下文、作用域到底是什么?二者有什么关系》) 的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。
举个例子:
在上述的例子中,其作用域伪代码如下:
因为静态作用域的特点,
foo
函数会从bar
中获取a
的值,但是这里的bar
已经执行完毕,执行上下文已经出栈了,a
变量肯定也不存在了,这就与我们闭包的定义矛盾了。所以这里闭包函数对外层函数的变量必定会特殊处理,以保证闭包函数可以使用,我们可以认为闭包就是将引用的外部函数的变量中记住了,类似于引用变量,存储在了“堆”中。用途
创建私有变量
闭包让函数以外也可以访问到函数内部的变量,这样可以用来创建私有变量,比如:
常见的用法就是防抖和节流函数:
模块化
通过匿名立即执行函数,单独开辟一个作用域,使得只暴露相关接口函数,达到模块化。
解决for循环中定时器函数取值异常问题
下文的代码中,我们期望在延迟函数中输出的是
1,2,3,4,5
,但实际上却并非如此:这是由于
setTimeout
内的函数发现自身i
这个变量未定义时,会作用域链去查找i
,而这里的i
是var
声明的,只会声明一次,也就是说后续i++
都会修改这个值,最终i
变成了6
,等到setTimeout
函数执行时,输出的自然都是6
。我们可以采用闭包的方式,单独开辟一个作用域给每次循环的
setTimeout
,这样输出就会跟我们所想的一致了。缺点
The text was updated successfully, but these errors were encountered: