首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 函数式vs.面向对象:响应未知和不确定
02 | 如何通过闭包对象管理程序中状态的变化?
03 | 如何通过部分应用和柯里化让函数具象化?
04 | 如何通过组合、管道和reducer让函数抽象化?
05|map、reduce和monad如何围绕值进行操作?
06 | 如何通过模块化、异步和观察做到动态加载?
07 | 深入理解对象的私有和静态属性
08|深入理解继承、Delegation和组合
09|面向对象:通过词法作用域和调用点理解this绑定
10|JS有哪8种数据类型,你需要注意什么?
11|通过JS引擎的堆栈了解闭包原理
12|JS语义分析该用迭代还是递归?
13 | JS引擎如何实现数组的稳定排序?
14 | 通过SparkPlug深入了解调用栈
15 | 如何通过哈希查找JS对象内存地址?
16 | 为什么环形队列适合做Node数据流缓存?
17 | 如何通过链表做LRU/LFU缓存?
18 | TurboFan如何用图做JS编译优化?
19 | 通过树和图看如何在无序中找到路径和秩序
20 | 算法思想:JS中分治、贪心、回溯和动态规划
21 | 创建型:为什么说Redux可以替代单例状态管理
22|结构型:Vue.js如何通过代理实现响应式编程
23 | 结构型:通过jQuery看结构型模式
24 | 行为型:通过观察者、迭代器模式看JS异步回调
25 | 行为型:模版、策略和状态模式有什么区别?
26|特殊型:前端有哪些处理加载和渲染的特殊“模式”?
27|性能:如何理解JavaScript中的并行、并发?
28|性能:通过Orinoco、Jank Busters看垃圾回收
29|网络:从HTTP/1到HTTP/3,你都需要了解什么?
30|安全:JS代码和程序都需要注意哪些安全问题?
31|测试(一):开发到重构中的测试
32|测试(二):功能性测试
33|测试(三):非功能性测试
34|静态类型检查:ESLint语法规则和代码风格的检查
35|Flow:通过Flow类看JS的类型检查
36|包管理和分发:通过NPM做包的管理和分发
37|编译和打包:通过Webpack、Babel做编译和打包
38|语法扩展:通过JSX来做语法扩展
39|Polyfill:通过Polyfill让浏览器提供原生支持
40|微前端:从MVC贫血模式到DDD充血模式
41|大前端:通过一云多端搭建跨PC/移动的平台应用
42|元编程:通过Proxies和Reflect赋能元编程
当前位置:
首页>>
技术小册>>
JavaScript进阶实战
小册名称:JavaScript进阶实战
### 02 | 如何通过闭包对象管理程序中状态的变化? 在JavaScript编程的进阶之旅中,理解和运用闭包(Closure)是一个重要的里程碑。闭包不仅是函数式编程的核心概念之一,也是管理程序状态、实现数据封装和创建私有变量等高级功能的关键工具。本章将深入探讨如何通过闭包对象来有效管理程序中状态的变化,包括闭包的基本概念、作用、实践应用以及如何利用闭包解决常见问题。 #### 一、闭包的基本概念 闭包,简而言之,是一个函数值,它引用了其外部作用域中的变量。这种引用让外部作用域的变量在函数执行完毕后依然能够被访问和修改,从而实现了状态的持久化。闭包的形成主要依赖于两个条件:一是函数嵌套函数,二是内部函数引用了外部函数的变量(包括参数)。 ```javascript function outerFunction(outerVar) { function innerFunction() { console.log(outerVar); // 访问外部函数的变量 } return innerFunction; // 返回内部函数,闭包形成 } const myClosure = outerFunction('Hello, Closure!'); myClosure(); // 输出: Hello, Closure! ``` 在上述例子中,`innerFunction`就是一个闭包,因为它引用了`outerFunction`作用域中的`outerVar`变量,并且即使`outerFunction`执行完毕,`outerVar`依然可通过`innerFunction`(即闭包)被访问。 #### 二、闭包管理状态的优势 1. **数据封装与隐藏**:通过闭包,可以创建私有变量,这些变量只能通过闭包内部的函数进行访问和修改,从而实现了数据的封装和隐藏,提高了代码的安全性和模块性。 2. **状态持久化**:闭包能够保持对外部作用域中变量的引用,即使外部函数已经执行完毕,这些变量也不会被垃圾回收机制回收,从而实现了状态的持久化。 3. **创建模块**:闭包是JavaScript模块化编程的基础之一,利用闭包可以模拟出类似其他语言中类的私有成员和公有方法的特性。 4. **实现高级功能**:如惰性加载(Lazy Loading)、缓存(Caching)、函数柯里化(Currying)等,这些功能都依赖于闭包来实现状态的灵活控制和管理。 #### 三、实践应用:使用闭包管理状态变化 ##### 3.1 计数器示例 闭包常被用于实现计数器,通过闭包可以确保每个计数器都有自己独立的状态,互不影响。 ```javascript function createCounter() { let count = 0; // 私有变量 return { increment: function() { count++; return count; }, decrement: function() { count--; return count; }, getCount: function() { return count; } }; } const counter1 = createCounter(); const counter2 = createCounter(); console.log(counter1.getCount()); // 0 counter1.increment(); console.log(counter1.getCount()); // 1 console.log(counter2.getCount()); // 0,counter2状态独立 ``` 在这个例子中,`createCounter`函数返回一个对象,该对象包含了三个方法:`increment`、`decrement`和`getCount`。这些方法通过闭包访问和修改`count`变量,实现了计数器的功能。由于每个调用`createCounter`时都会创建一个新的作用域,因此每个计数器实例都有自己独立的`count`变量。 ##### 3.2 私有成员与公共接口 闭包还可以用来模拟面向对象编程中的私有成员和公共接口。 ```javascript function Person(name, age) { let _name = name; let _age = age; function updateName(newName) { _name = newName; } function updateAge(newAge) { if (typeof newAge === 'number' && newAge >= 0) { _age = newAge; } } function getName() { return _name; } function getAge() { return _age; } return { updateName: updateName, updateAge: updateAge, getName: getName, getAge: getAge }; } const alice = Person('Alice', 30); console.log(alice.getName()); // Alice alice.updateAge(31); console.log(alice.getAge()); // 31 ``` 在这个`Person`函数中,`_name`和`_age`作为私有变量,只能通过返回的对象中定义的公共方法来访问和修改。这种方式增强了数据的安全性,防止了外部代码直接修改内部状态。 #### 四、闭包的高级应用 ##### 4.1 惰性加载 惰性加载是一种优化技术,它延迟了某些操作的执行,直到真正需要时才进行。闭包是实现惰性加载的一种有效方式。 ```javascript function createLazyLoader(filePath) { let data = null; return function() { if (!data) { // 假设这里是通过异步方式加载数据 data = "Loaded data from " + filePath; } return data; }; } const loader = createLazyLoader('example.json'); console.log(loader()); // 首次调用,加载数据 console.log(loader()); // 再次调用,返回已加载的数据 ``` ##### 4.2 缓存 利用闭包,我们可以实现简单的缓存机制,避免重复执行耗时的操作。 ```javascript function memoize(fn) { let cache = {}; return function(...args) { const key = args.join(','); if (cache[key]) { return cache[key]; } const result = fn.apply(this, args); cache[key] = result; return result; }; } const fibonacci = memoize(function(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); }); console.log(fibonacci(10)); // 使用缓存,避免重复计算 ``` #### 五、闭包的注意事项 1. **内存泄漏**:虽然闭包可以保持状态的持久化,但如果不当使用,也可能导致内存泄漏。如果闭包引用的外部变量不再需要,但这些变量仍被闭包内的函数所引用,那么这些变量将不会被垃圾回收。因此,需要特别注意闭包中的变量引用,及时释放不再需要的资源。 2. **性能考虑**:闭包会增加函数的调用开销,因为每次调用闭包函数时,都需要创建一个新的作用域链。在性能敏感的应用中,需要权衡闭包带来的便利与可能的性能损失。 #### 结语 通过本章的学习,我们深入理解了闭包的基本概念、作用以及在管理程序中状态变化方面的优势和应用。闭包不仅是JavaScript中一个强大的特性,也是实现高级编程模式和优化应用性能的重要工具。希望读者能够熟练掌握闭包的使用技巧,并在实际开发中灵活运用,创造出更加高效、安全、可维护的代码。
上一篇:
01 | 函数式vs.面向对象:响应未知和不确定
下一篇:
03 | 如何通过部分应用和柯里化让函数具象化?
该分类下的相关小册推荐:
ES6入门指南
剑指javascript-ES6
编程入门课:Javascript从入门到实战
Flutter核心技术与实战
JavaScript入门与进阶
Node.js 开发实战
Javascript-ES6与异步编程
KnockoutJS入门指南
web前端开发性能优化实战
深入学习前端重构知识体系
Javascript重点难点实例精讲(一)
经典设计模式Javascript版