Javascript中的闭包

文章目录

闭包是 javascript 中最重要但经常被误解的概念之一。理解闭包可以解锁其他情况下难以使用的编程模式。在这篇文章中,我们将了解什么是闭包,并通过几个例子来巩固我们对它们的理解。

函数的生命周期

首先我们来看一个简单的函数:

function getValue(){
  var a = 1
  var b = 2
  return a + b
}

var val = getValue()
console.log(val)
// Output: 3

getValue 函数实例化两个变量(ab),并返回它们的和。将此返回值存储在 val 变量中,然后打印出来。

花点时间思考一下 getValue 函数返回后 ab 的命运。

这些变量仅在函数范围内可见,你不能在 getValue 函数体之外访问 ab。一旦 getValue 函数返回,就无法获取 ab 的值,它们会被垃圾回收。

闭包实战

现在,让我们看一个不一样的函数:

function newCounter(){
  var count = 0

  return function(){
    count += 1
    return count
  }
}

var counter = newCounter()

console.log(counter())
// Output: 1

console.log(counter())
// Output: 2

console.log(counter())
// Output: 3

newCounter 的返回值是一个函数。此函数增加 count 的值(其范围在 newCounter 函数体内)并返回它。

与上一个例子类似,没有人可以在 newCounter 函数体之外访问 count 的值。但是,与上一个示例不同的是,newCounter 返回的函数仍然可以访问 counter。这没有违反任何规则,因为返回函数的主体仍在 newCounter 的主体内。

因此,返回的函数可以间接访问 count(在我们的示例中,它被分配给 counter 变量),但它仍然与程序中想要访问其值的任何其他代码隔离开来。 这个将 count 变量封闭起来的函数体称为闭包。

这与之前示例中的变量 ab 有何不同?由于在 getValues 闭包中没有任何其他函数引用 ab,它们将会被垃圾回收,不再存在。而 count 变量将仍然存在于 newCounter 的闭包中,因为它被返回的函数引用了,所以在 newCounter 返回后,它仍然存在于系统内存中。

更多示例

在我看来,理解闭包的最好方法是看它们的实际应用。让我们试着通过几个例子来理解它们是如何工作的:

function greeting(){
  var hello = 'hello'
  var world = 'world'

  function join(){
    return hello + ' ' + world
  }

  return join()
}

console.log(greeting())
// Output: hello world

这看起来与我们之前的示例非常相似,但是这里没有闭包!这是因为与 newCounter 不同,join 函数虽然可以访问 helloworld 变量,但它在 greeting 函数体中被调用并退出。greeting 返回的是调用 join 函数的执行结果而不是 join 函数本身。

如果我们稍微修改函数以返回 join 函数,而不是它的结果,那么 helloworld 将仍然存在于 greeting 的闭包中:

function greeting(){
  var hello = 'hello'
  var world = 'world'

  function join(){
    return hello + ' ' + world
  }

  return join
}

var sayGreeting = greeting()

console.log(sayGreeting())
// Output: hello world

让我们看另一个例子,我们使用对象方法来获取返回值:

function Counter(){
  this.count = 0
  this.getCount = function(){
    this.count += 1
    return this.count
  }
}

var counter = new Counter()

console.log(counter.getCount())
// Output: 1

console.log(counter.getCount())
// Output: 2

同样,这似乎与我们之前的计数器示例在做同样的事情,但这里的关键区别在于 this.count 没有封闭在 Counters 闭包内,我们仍然可以访问它:

console.log(counter.count)
// Output: 2

如果我们修改代码使 count 成为一个变量而不是一个对象属性,那么它的作用域将被限制在 Counters 闭包中:

function Counter(){
  var count = 0
  this.getCount = function(){
    count += 1
    return count
  }
}

var counter = new Counter()

console.log(counter.getCount())
// Output: 1

console.log(counter.getCount())
// Output: 2

console.log(counter.count)
// Output: undefined

也可以看看


全国大流量卡免费领

19元月租ㆍ超值优惠ㆍ长期套餐ㆍ免费包邮ㆍ官方正品