定义
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。
也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
闭包是由函数以及声明该函数的词法环境组合而成的。该环境包含了这个闭包创建时作用域内的任何局部变量。[MND]
优点
- 可以从内部函数访问外部函数的作用域中的变量,且访问到的变量长期驻扎到内存中;
- 避免变量污染全局;
- 把变量存到独立的作用域中,作为私有变量存在;
缺点
- 对内存消耗有负面影响。因内部函数保存了对外部变量的引用,导致无法被垃圾回收,增大内存使用量,所以使用不当会导致内存泄漏
- 对处理速度具有负面影响。闭包的层级决定了引用的外部变量在查找时经过的作用域链长度
- 可能获取到意外的值(captured value)
使用场景
私有变量
var PrivateName = function () {
var privateName = ""
function Name () {}
Name.prototype.set = function(name) {
privateName = name
}
Name.prototype.say = function() {
console.log("My name is " + privateName)
}
return Name
}()
var myname = new PrivateName()
myname.set("小明")
myname.say()
单例模式
假设我们要设计一个HTTP的请求,所有从这个实例发出的请求都要带一个版本号。
var MyRequest = function(){
var instance = null
return function () {
if (!instance) {
instance = new XMLHttpRequest()
instance.setRequestHeader("Version", "1.0.0")
//....
}
return instance
}
}();
// 这样每一个通过 MyRequest 实例发出的请求都会带有版本号的请求头
var myRequest1 = new MyRequest()
var myRequest2 = new MyRequest()
设计一个自增ID
var getID = function(){
var id = 0
return function () {
console.log(id)
return id++
}
}();
var id1 = getID()
var id2 = getID()
思考如何拿到正确的值
for(var i=0; i<10; i++) {
setTimeout(function() {
console.log(i)
}, 1000)
}
查看解析
for(var i = 0; i < 10; i++) {
(function (g) {
setTimeout(function() {
console.log(g)
}, 1000)
})(i);
}
如何实现防抖和节流
查看解析
function debounce(fun, delay) {
let timer = null
return function() {
clearTimeout(timer)
const args = arguments
const that = this
timer = setTimeout(function() {
fun.apply(that, args)
}, delay)
}
}
function throttle(fun, delay) {
let timer = null
return function() {
if (timer !== null) return
const args = arguments
const that = this
timer = setTimeout(function() {
fun.apply(that, args)
clearTimeout(timer)
timer = null
}, delay)
}
}