ch05 作用域闭包

理解闭包可以看作是某种意义上的重生,需要付出非常多的努力和牺牲才能理解这个概念。
需要根据自己的意愿来识别、拥抱和影响闭包的思维环境。

★★★ 闭包的定义

闭包可以理解为在当前函数内部定义另一个函数,
该函数除了用于自身的作用域外还能保持对外部作用域的引用,并且能够作为返回值返回。
在当前作用域外调用该函数时,可以访问外层函数作用域的变量。

在当前作用域外调用该函数的时候,可以访问外部函数作用域的变量。(闭包其中一个重要条件是:闭包是在词法作用域以外执行的)

=》 eg

function foo() {
var a = 2;
function bar() {
console.log(a); // 2
}
bar();
}
foo();
// bar()可以访问外层作用域中的变量a

闭包 - 典型案例

function foo() {
var a = 2;
function bar() {
console.log(a);
}
return bar;
}
var baz = foo();
baz(); // 2 (我亲爱的朋友,这就是闭包的效果!)

闭包的使用: 在定时器、事件监听器、Ajax请求、跨窗口通信、Web Workers或任何其他的异步任务中,只要使用回调函数就是在使用闭包!!!

模块(Module) => 利用闭包来实现(★★★★★★)

  • 常规代码
function foo() {
var something = 'cool';
var another = [1, 2, 3];
function doSomething() {
console.log(something);
}
function doAnother() {
console.log(another.join(' ! ');
}
}
// 此时并没有明显的闭包,只有两个私有数据变量something和another 以及doSomething()和doAnother()两个内部函数
// 词法作用域就是foo()的内部作用域
  • 使用闭包改造上述代码
function CoolModule() {
var something = 'cool';
var another = [1, 2, 3];
function doSomething() {
console.log(something);
}
function doAnother() {
console.log(another.join(' ! ');)
}
return {
doSomething: doSomething,
doAnother: doAnother,
}
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

这个模式在JS中称之为模块,最常见模块模式的方法称之为【模块暴露】
CoolModule()只是一个函数,必须通过调用它来创建一个模块实例,如果不执行外部函数,内部作用域和闭包都无法被创建
其次CoolModule()返回一个用对象字面量语句 =》 {key:value, ...}来表示对象。
返回的对象中含有对内部函数而不是内部数据变量的引用。保持内部数据变量是隐藏且私有的状态。
可以将这个对象类型的返回值看作本质上是模块的公共API。

doSomething()和doAnother()函数具有涵盖模块实现内部作用域的闭包(调用CoolModule()实现)
当通过返回一个含有属性引用的对象的方式来将函数传递到词法作用域外部的时候创造可以观察和实践闭包的条件;

模块模式需要具备两个必要条件》》》

1. 必须要有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)。
2. 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或修改私有的状态。

一个具有函数属性的对象并不是真正的模块,一个从函数调用所返回的,只有数据属性而没有闭包函数的对象并不是真正的模块。

上述的实例代码中有一个叫做CoolModule()的独立模块创建器,可以被调用任意多次,每次调用都会创建一个新的模块实例。
当只需要一个实例的时候可以对这个模块进行简单的改进来实现单例模式:

const foo = (function CoolModule() {
var something = 'cool';
var another = [1, 2, 3];
function doSomething() {
console.log(something);
}
function doAnother() {
console.log(another.join(' ! '));
}
return {
doSomething: doSomething,
doAnother: doAnother,
}
})();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

将模块函数转换成IFEE,立即调用这个函数并将返回值直接赋值给单例的模块实例标识符foo

function CoolModule(id) {
function identify() {
console.log(id);
}
return {
identify: identify
};
}
var foo01 = CoolModule('foo 1');
var foo02 = CoolModule('foo 2');
foo01.identify(); // 'foo 1'
foo02.identify(); // 'foo 2'

通过模块实例的内部保留对公共API对象的内部引用,可以从内部模块实例进行修改,包括添加或删除方法和属性以及修改它们的值

现代模块化机制

模块依赖加载器/管理器本质上是将这种模块定义封装进一个友好的API。

var MyModules = (function Manager() {
var modules = {}
// 代码核心: modules[name] = impl.apply(impl, deps) =》 为模块的定义引入包装函数(可以传入任何依赖并返回值或模块API存储在一个根据名字来管理的模块列表中)
function define(name, deps, impl) {
for(var i = 0; i < deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply(impl, deps);
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get,
}
})

如何使用上述代码定义模块

MyModules.define('bar', [], function() {
function hello(who) {
return 'Let me introduce: ' + who;
}
return {
hello: hello
};
});
MyModules.define('foo', ['bar'], function(bar) {
var hungry = 'hippo';
function awesome() {
console.log(bar.hello(hungry).toUpperCase());
}
return {
awesome: awesome
}
});
var bar = MyModules.get('bar');
var foo = MyModules.get('foo');
console.log(bar.hello('hippo');

未来的模块化机制 - ES6将文件当作独立的模块进行处理,每个模块都可以导入其他模块或特定的API成员

# bar.js
function hello(who) {
return 'Let me introduce: ' + who;
}
export hello;
# foo.js
// 仅仅从bar模块导入hello
import hello from 'bar';
var hungry = 'hippo';
function awesome() {
console.log(hello(hungry).toUpperCase());
}
export awesome;
// baz.js
// 导入完整的 'foo' 和 'bar'模块
module foo from 'foo';
module bar from 'bar';
console.log(bar.hello('rhino'));
foo.awesome();

总结

什么时候产生闭包? 当函数是在当前词法作用域之外执行的时候就产生闭包!!!

posted @   Felix_Openmind  阅读(10)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
*{cursor: url(https://files-cdn.cnblogs.com/files/morango/fish-cursor.ico),auto;}
点击右上角即可分享
微信分享提示