引言
闭包这个词对很多前端开发人员来说既熟悉又陌生,熟悉是因为很多人都用过闭包,但是用的时候不知道闭包,陌生是因为并不理解闭包,接下来这篇文章将会从多方面介绍闭包
定义
闭包是怎么定义的呢?当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数在当前词法作用域之外执行。来看一个具体例子:
function foo () {
var a = 2
function bar () {
console.log(a)
}
return bar
}
var baz = foo()
baz() //2
复制代码
函数bar的词法作用域可以访问foo的内部作用域,并且bar在被作为返回值赋值给baz执行时,bar函数在定义时的词法作用域以外的地方被调用,依然可以访问foo函数的内部作用域变量a,这就是闭包
分析
现在让我们来看为什么闭包可以在定义的词法作用域外记住并且访问定义时的词法作用域的变量,想要一探究竟,先来看一个简单的例子来函数的执行过程:
function foo (a) {
console.log(a)
}
foo (a)
复制代码
function foo () {
var a = 2
function bar (b) {
console.log(a + b)
}
return bar
}
var baz = foo()
baz(3) //5
复制代码
常见问题
说到闭包相关的问题,最典型的就是变量和this指向这两类问题。
变量
function test () {
var result = new Array()
for (var i = 0; i < 6; i++) {
result[i] = function () {
return i
}
}
return result
}
复制代码
function test () {
var result = new Array()
for (var i = 0; i < 6; i++) {
result[i] = (function () {
return i
})()
}
return result
}
复制代码
将闭包直接改成一个自执行函数,自执行函数本身是没有变量作用域的,因此会使用外层函数的变量作用域,这样也能达到我们想要的效果
this指向
var name = "window"
var obj = {
name: "object",
getName: function () {
return function () {
return this.name
}
}
}
console.log(obj.getName()())
复制代码
上面这段js代码的this.name的返回值是window,这是为什么呢?按照上面写到的,此匿名函数在执行过程中,它的作用域会包含三部分:自身的活动对象、getName函数的活动对象和全局的变量对象,同时每个活动对象自动取得两个特殊的变量:this和arguments,但是内部函数在查找this时是无法直接访问外部函数的this变量,因此会沿着作用域链去查找全局变量中继续查找,如果想要取外部函数中的this取值也很简单,只需要向下面代码这样:
var name = "window"
var obj = {
name: "object",
getName: function () {
var that = this
return function () {
return that.name
}
}
}
console.log(obj.getName()())
复制代码
将this赋值给一个变量,内部函数是可以访问外部函数变量的,这样就解决了
总结
闭包是一个容易混淆不清的概念,这篇文章对闭包的定义、执行、常见问题做了简单的介绍,希望通过这篇能对大家理解和使用闭包有所帮助。如果有错误或不严谨的地方,欢迎批评指正,如果喜欢,欢迎点赞。