作用域
想掌握闭包那么就一定要知道什么是作用域。我们先看下面这段代码:
代码语言:javascript复制function foo() {
var a = 2;
console.log(a);
}
foo.a; // undefined
在ES6之前,只有函数可以创建作用域,这里函数foo
创建了一个作用域并在该作用域里声明了一个变量a
。我们发现,在函数外部是没有办法访问到该变量。作用域的作用之一就是控制变量的访问范围。作用域另外一个作用就是约束了变量的生命周期,也就是说函数执行完毕后作用域内的所有变量都会被销毁
作用域链
上面我们说到作用域控制了变量的访问范围,在作用域外无法访问到作用域里的变量。但是如果是由内而外访问是没有问题的,看下面这段示例代码:
代码语言:javascript复制function foo() {
var a = 2;
function bar() {
console.log(a);
}
bar();
}
foo(); // 2
是的,JavaScript
允许函数里声明函数,也就是说,在作用域里创建子作用域,这就是作用域链。而这种嵌套的方式正是闭包
闭包
那作用域和闭包是什么关系呢?闭包英文是“Closure”,中译“关闭”。前面说到内部作用域可以访问上级作用域的变量,外部无法访问内部的作用域。对于外部来说,内部作用域就像是一个封闭的包裹,那有什么办法让外部访问内部呢?
代码语言:javascript复制function foo() {
var a = 2;
function bar() {
return {
a
};
}
return bar;
}
var baz = foo()(); // { a: 2 }
我们将函数作为返回值返回,发生了一个奇怪的现象,我们试想,这不就把内部作用域传递到外部了吗?那外部是不是可以由此访问里面嵌套的作用域了吗
闭包是如何产生的
产生闭包的条件:
- 嵌套函数
- 内部函数持有外部函数的变量
生命周期
嵌套的内部函数执行完会去销毁闭包
代码语言:javascript复制function foo() {
var a = 2;
bar();
function bar() {
console.log( a);
}
}
foo(); // 3
foo(); // 3
实际应用
模块化
闭包是模块化开发的基石,代码如下所示:
代码语言:javascript复制var module1 = (function(){
var _count = 0;
var m1 = function(){
//...
};
var m2 = function(){
//...
};
return {
m1 : m1,
m2 : m2
};
})();
防抖
我们在搜索框输入关键词时,如果每输入一个字都去远程加载是非常浪费资源的。防抖可以很好的解决这个问题,在一个延时函数里去实现请求,如果关键词变更了,则清除之前的请求,重新延时加载
代码语言:javascript复制function debounce(fun, delay) {
return function (args) {
let that = this
let _args = args
clearTimeout(fun.id)
fun.id = setTimeout(function () {
fun.call(that, _args)
}, delay)
}
}
function ajax() {}
debounce(ajax, 300);
节流
当我们提交表单的时候,如果误触多次,那么就会导致表单重复提交。节流的做法就是规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效
代码语言:javascript复制 function throttle(fun, delay) {
let last, deferTimer
return function (args) {
let that = this
let _args = arguments
let now = new Date()
if (last && now < last delay) {
clearTimeout(deferTimer)
deferTimer = setTimeout(function () {
last = now
fun.apply(that, _args)
}, delay)
}else {
last = now
fun.apply(that,_args)
}
}
}