Skip to content

AI编程自学网

ai2opencode您的编程助手

Menu
  • 网站首页
  • WordPress专栏
  • 程序员内功
  • 副业和涨工资
  • Windows11
  • Flutter 教程专栏
Menu

Javascript问答之 JavaScript 闭包是如何工作的?

Posted on 2021年7月30日 by ai2opencode

实战问题

你会如何向了解 JavaScript 闭包组成的概念(例如函数、变量等)但不了解闭包本身的人解释 JavaScript 闭包?

我看过维基百科上给出的 Scheme 例子,但不幸的是它没有帮助。

解决方案

闭包是一对:

  • 一个函数
  • 对该函数外部作用域的引用(词法环境)

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

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

## 面向事件的编程
在下面的代码中, functiononClick关闭了 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)

展开片段
## 模块化
在以下示例中,所有实现细节都隐藏在立即执行的函数表达式中。函数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.counter
counter.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() // 43
o.update(5)
o.log() // 5
const p = createObject()
p.log() // 42

示例 3
如果您正在使用 using 声明的变量var,请注意您了解要关闭的变量。使用声明的变量var被提升。由于引入了let和,这在现代 JavaScript 中的问题要小得多const。

在下面的代码中,每次循环时,inner都会创建一个新函数,它关闭i. 但是因为var i是在循环外提升的,所有这些内部函数都关闭了同一个变量,这意味着i(3)的最终值被打印了 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(…)在函数内使用( Function 构造函数) 时,它不会关闭其词法环境:而是关闭全局上下文。新函数不能引用外部函数的局部变量。
JavaScript 中的闭包就像在函数声明点保留对作用域的引用(而不是副本),而后者又保留对其外部作用域的引用,依此类推,一直到顶部的全局对象作用域链。
声明函数时会创建一个闭包;这个闭包用于在调用函数时配置执行上下文。
每次调用函数时都会创建一组新的局部变量。

Related posts:

  1. JavaScript 技巧之 如果你不知道的console对象方法是相当惊人的
  2. Javascript问答之 JavaScript 中的“use strict”有什么作用,背后的原因是什么?
  3. 七个代码更出色的 JavaScript技巧之 01 … 点差运算符
  4. 微服务之使用 SpringBoot 构建 Rest 微服务的最佳实践

发表回复 取消回复

要发表评论,您必须先登录。

工具区

繁体中文

近期文章

  • 通识基础之为什么π是宇宙中最重要的常数,无处不在的常数
  • 站立式办公桌是在浪费时间吗?为什么站着工作对健康的好处可能有点被夸大了?
  • 解题的三大法则,解决广泛领域问题的工具
  • (无标题)
  • 不要只是设定目标,构建系统,幸福和成就更多的秘诀

近期评论

  1. J1o! - V2EX-Flutter 您应该选择哪个 IDE/编辑器?(Android Studio VS Code Intellij IDEA)发表在Flutter 您应该选择哪个IDE/编辑器?(Android Studio VS Code Intellij IDEA)
  2. 编程书籍推荐之《Think Like a Programmer: An Introduction to Creative Problem Solvin》 - AI编程自学网发表在如何解决任何编程问题

    推荐文章

    1. JavaScript 技巧之 如果你不知道的console对象方法是相当惊人的
    2. Javascript问答之 JavaScript 中的“use strict”有什么作用,背后的原因是什么?
    3. 七个代码更出色的 JavaScript技巧之 01 … 点差运算符
    4. 微服务之使用 SpringBoot 构建 Rest 微服务的最佳实践
    • 0经验开发
    • Access
    • adsense
    • Android
    • App开发赚钱
    • AWS云计算
    • Chrome
    • Chrome 控制台实用程序开发
    • CSS
    • CSS 基础教程
    • Dart语言
    • Flutter
    • Flutter基础
    • Flutter杂谈
    • HarmonyOS 鸿蒙
    • HarmonyOS基础
    • HTML
    • HTML基础
    • HTML技巧
    • JavaScript
    • JavaScript 基础
    • JavaScript 技巧
    • JavaScript 简介
    • JavaScript问答
    • oracle
    • oracle
    • pandas教程
    • PHP
    • PHP 杂谈
    • Python
    • Python实战
    • Python技巧
    • Python杂谈
    • SEO 技巧
    • Tiktok抖音小程序
    • UI设计
    • Web编程
    • Windows11
    • WordPress
    • WordPress 部署云主机VPS
    • WordPress 问答
    • WordPress 问答已解决
    • WordPress 问答未解决
    • WordPress使用技巧
    • WordPress插件
    • WordPress杂谈
    • WordPress盈利
    • Wordpress配置
    • WorPress建站技巧
    • 云服务推广
    • 云计算
    • 人工智能
    • 人工智能与机器学习
    • 低代码与无代码
    • 信息论基础
    • 健康工作方式
    • 健康生活
    • 元宇宙
    • 副业和涨工资
    • 副业技巧
    • 在线课程
    • 学习编程技巧
    • 小程序
    • 建站指南
    • 微服务架构
    • 微软
    • 思想类
    • 技术文章技巧
    • 技术潮流
    • 技能考试
    • 抖音小程序
    • 教学方法
    • 教学策略
    • 教学经验
    • 教育信息化
    • 教育案例与方法
    • 教育趋势
    • 数学大师
    • 数学学习
    • 数据库
    • 未分类
    • 程序员内功
    • 程序员装备
    • 经典书籍学习
    • 编程书籍推荐
    • 编程书籍推荐
    • 编程人生
    • 编程历史
    • 编程市场研究
    • 编程思想
    • 编程意义
    • 编程组件
    • 编程能力提高
    • 编程语言
    • 编程面试与工作
    • 网站合集
    • 腾讯云
    • 视频博主
    • 计算机科学中的数学
    • 读书与听书
    • 读书笔记
    • 软件估价
    • 软考
    • 通识知识
    • 量子计算
    • 销售 API
    • 阿里云
    • 高级信息系统项目管理师
    • 高级系统架构设计师

    dart Discord flutter JavaScript SpringBoot windows11 元宇宙 微服务 程序员内功 计算机视觉 问题未解决

    登录
    © 2023 AI编程自学网 | Powered by Minimalist Blog WordPress Theme