栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

JavaScript闭包如何工作?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JavaScript闭包如何工作?

闭包是以下内容的配对:

  1. 一个功能,以及
  2. 对函数外部范围的引用(词法环境)

词法环境是每个执行上下文(堆栈框架)的一部分,并且是标识符(即局部变量名称)和值之间的映射。

Javascript中的每个函数都对其外部词汇环境保持引用。此引用用于配置调用函数时创建的执行上下文。此引用使函数内部的代码可以“查看”在函数外部声明的变量,而不管调用函数的时间和位置。

如果一个函数由一个函数调用,而另一个函数又调用了另一个函数,则将创建对外部词汇环境的引用链。该链称为作用域链。

在下面的代码中,

inner
foo
调用时创建的执行上下文的词法环境形成一个闭包, 变量进行 闭包
secret

function foo() {  const secret = Math.trunc(Math.random()*100)  return function inner() {    console.log(`The secret number is ${secret}.`)  }}const f = foo() // `secret` is not directly accessible from outside `foo`f() // The only way to retrieve `secret`, is to invoke `f`

换句话说:在Javascript中,函数带有对私有“状态框”的引用,只有它们(以及在相同词法环境中声明的任何其他函数)可以访问。状态框对于函数的调用者是不可见的,从而为数据隐藏和封装提供了一种出色的机制。

请记住:Javascript中的函数可以像变量一样传递(一流的函数),这意味着功能和状态对可以在程序中传递:类似于在C ++中传递类的实例的方式。

如果Javascript没有闭包,则必须在函数之间 显式 传递更多状态,从而使参数列表更长,代码更嘈杂。

因此,如果您希望函数始终有权访问私有状态,则可以使用闭包。

......频频我们 希望与功能关联状态。例如,在Java或C ++中,当您将私有实例变量和方法添加到类时,您正在将状态与功能相关联。

在C语言和大多数其他常见语言中,函数返回后,所有本地变量将不再可访问,因为堆栈框架被破坏了。在Javascript中,如果您在另一个函数中声明一个函数,则外部函数的本地变量在返回后仍可访问。这样一来,在上面的代码,

secret
仍然可用的函数对象
inner

之后 它已经从返回
foo

闭包的使用

每当需要与函数关联的私有状态时,闭包都是有用的。这是一种非常常见的情况-
请记住:Javascript直到2015年才使用类语法,并且仍然没有私有字段语法。封闭件可满足此需求。

私有实例变量

在以下代码中,函数

toString
关闭了汽车的详细信息。

function Car(manufacturer, model, year, color) {  return {    toString() {      return `${manufacturer} ${model} (${year}, ${color})`    }  }}const car = new Car('Aston Martin','V8 Vantage','2012','Quantum Silver')console.log(car.toString())

功能编程

在以下代码中,函数同时

inner
关闭
fn
args

function curry(fn) {  const args = []  return function inner(arg) {    if(args.length === fn.length) return fn(...args)    args.push(arg)    return inner  }}function add(a, b) {  return a + b}const curriedAdd = curry(add)console.log(curriedAdd(2)(3)()) // 5

面向事件的程序

在以下代码中,函数

onClick
在variable之上关闭
BACKGROUND_COLOR

const $ = document.querySelector.bind(document)const BACKGROUND_COLOR = 'rgba(200,200,242,1)'function onClick() {  $('body').style.background = BACKGROUND_COLOR}$('button').addEventListener('click', onClick)<button>Set background color</button>

模块化

在下面的示例中,所有实现细节都隐藏在立即执行的函数表达式中。这些功能

tick
以及
toString
它们完成工作所需的私有状态和功能关闭。封闭使我们能够模块化和封装我们的代码。

let namespace = {};(function foo(n) {  let numbers = []  function format(n) {    return Math.trunc(n)  }  function tick() {    numbers.push(Math.random() * 100)  }  function toString() {    return numbers.map(format)  }  n.counter = {    tick,    toString  }}(namespace))const counter = namespace.countercounter.tick()counter.tick()console.log(counter.toString())

例子

例子1

此示例显示局部变量未在闭包中复制:闭包维护对原始变量 本身 的引用。好像在外部函数退出后,堆栈框架仍在内存中保持活动状态。

function foo() {  let x = 42  let inner  = function() { console.log(x) }  x = x+1  return inner}var f = foo()f() // logs 43

例子2

在下面的代码,三种方法

log
increment
update
所有密切在同一词法环境。

每次

createObject
调用时,都会创建一个新的执行上下文(堆栈框架),并创建一个全新的变量
x
,并创建一组新的函数(
log
等),这些函数将覆盖此新变量。

function createObject() {  let x = 42;  return {    log() { console.log(x) },    increment() { x++ },    update(value) { x = value }  }}const o = createObject()o.increment()o.log() // 43o.update(5)o.log() // 5const p = createObject()p.log() // 42

例子3

如果您使用的是使用声明的变量

var
,请务必了解要关闭的变量。使用声明的变量
var
被提升。这是非常现代的Javascript的问题较少,由于引进
let
const

在以下代码中,每次循环时,

inner
都会创建一个新函数,该函数关闭
i
。但是由于
vari
悬挂在循环外部,所有这些内部函数都在同一变量上闭合,这意味着将
i
(3)的最终值打印了三遍。

function foo() {  var result = []  for (var i = 0; i < 3; i++) {    result.push(function inner() { console.log(i) } )  }  return result}const result = foo()// The following will print `3`, three times...for (var i = 0; i < 3; i++) {  result[i]()}

最后一点:

  • 每当在Javascript中声明函数时,都会创建一个闭包。
  • function
    从另一个函数内部返回a 是闭包的经典示例,因为外部函数内部的状态对于返回的内部函数是隐式可用的,即使在外部函数完成执行之后也是如此。
  • 每当
    eval()
    在函数内部使用时,都会使用闭包。您
    eval
    可以在文本中引用函数的局部变量,在非严格模式下,甚至可以使用来创建新的局部变量
    eval('var foo = …')
  • 当您
    new Function(…)
    在[函数]内部使用([函数构造函数])时,它不会关闭其词法环境:而是关闭全局上下文。新函数不能引用外部函数的局部变量。
  • Javascript中的闭包就像在函数声明时保留对作用域的引用(而 不是 副本),这继而保留对外部作用域的引用,依此类推,一直指向全局对象的顶部。范围链。
  • 声明函数时创建一个闭包。当调用函数时,此闭包用于配置执行上下文。
  • 每次调用函数时都会创建一组新的局部变量。


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/390603.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号