闭包是一个学起来令人困惑的 JavaScript 概念,因为很难看出它们是如何被实际使用的。
与函数 、 对象 , , , 不 直接 使用 使用 不 不 不 : : : 哦 在 这里 这里 这里 这里 这里 ,
但 但 , , 这 闭包 学习 的 的 确定 确定 确定 何时 何时 何时 何时 何时 何时 何时
JavaScript 中的闭包是什么
Bạn sẽ không bao giờ làm điều đó với bất kỳ ai trong số họ.
const value = 1
function doSomething() {
let data = [1,2,3,4,5,6,7,8,9,10,11]
return data.filter(item => item % value === 0)
}
这里函数 doSomething
使用变量 value
。但是函数 item => item % value === 0
也可以被写成:
function(item){
return item % value === 0
}
你使用在函数外部定义的变量 value
的值。
函数读取外部的值
与前面的示例一样,函数可以访问和使用在其“主体”或上下文之外定义的值,例如:
let count = 1
function counter() {
console.log(count)
}
counter() // print 1
count = 2
counter() // print 2
这允许我们从模块中的任何地方修改 count
变量的值。那么当计数器函数被调用时,它就会知道如使用当前的值。
我们为什么使用函数
但是 为什么 在 使用 , , , 我们 函数 函数 函数 函数 函数 函数 函数 函数
想象一段代码,它做了一些很棒的事情,它有 X 行代码。
/* My wonderful piece of code */
现在假设你必须在程序的各个部分使用这段精彩的代码,你会怎么做?
自然 自然 的 段 段 段 , , , , , , , 重用 ,
现在,你可以尽可能多地使用你的函数。而且,忽略某些特殊情况,调用你的函数 N 次与编写那段优美的代码 N 次是一样的。这是一个简单的替换。
在哪里使用闭包呢
使用计数器的示例,让我们将它当作那段优美的代码。
let count = 1
function counter() {
console.log(count)
}
counter() // print 1
现在,我们想在很多地方重用它,所以我们将它“包装”在一个函数中。
function wonderfulFunction() {
let count = 1
function counter() {
console.log(count)
}
counter() // print 1
}
现在我们有什么?一个函数:counter
,使用在它之外声明的值 count
。还有一个值:count
,它是在 wonderfulFunction
函数作用域中声明的,但在 counter
函数中使用。
也就是说,我们有一个函数,它使用了一个在其外部声明的值:一个闭包。
很简单,不是吗?现在,当执行 WonderFunction
函数时会发生什么?一旦父函数被执行,变量 count
和函数 counter
会发生什么变化?
在其主体中声明的变量和函数会“消失”(垃圾收集器)。
现在,让我们稍微修改一下示例:
function wonderfulFunction() {
let count = 1
function counter() {
count++
console.log(count)
}
setInterval(counter, 2000)
}
wonderfulFunction()
在 wonderfulFunction
内部声明的变量和函数现在会发生什么?
在这个例子中,我们告诉浏览器每 2 秒运行一次 counter
。因此,JavaScript 引擎必须保留对函数及其使用的变量的引用。即使在父函数 wonderFunction
完成其执行周期后,函数 counter
和值 count
仍将“存活”。
之所以会出现这种具有闭包的“效果”,是因为 JavaScript 支持函数的嵌套。或者换句话说,函数是语言中的一等公民,你可以像使用任何其他对象一样使用它们:嵌套、为参数传递、作为返回值等等。
我可以在 JavaScript 中使用闭包做什么
立即调用函数表达式(IIFE)
这 是 种 种 在 在 时代 技术 技术 的 的 的 的 的 在此之前 在此之前 在此之前 包装 包装 包装 个 立即 立即 立即 立即 立即 立即
(function(arg1, arg2){
...
...
})(arg1, arg2)
这使你可以使用只能由模块本身在函数内使用的私有变量——也就是说,允许模拟访问修饰符。
const module = (function(){
function privateMethod () {
}
const privateValue = "something"
return {
get: privateValue,
set: function(v) { privateValue = v }
}
})()
var x = module()
x.get() // "something"
x.set("Another value")
x.get() // "Another Value"
x.privateValue //Error
函数工厂
另一个通过闭包实现的设计模式是“函数工厂”,即函数创建函数或对象,例如,允许数创建函数幨淕
const createUser = ({ userName, avatar }) => ({
id: createID(),
userName,
avatar,
changeUserName (userName) {
this.userName = userName;
return this;
},
changeAvatar (url) {
// execute some logic to retrieve avatar image
const newAvatar = fetchAvatarFromUrl(url)
this.avatar = newAvatar
return this
}
});
console.log(createUser({ userName: 'Bender', avatar: 'bender.png' }));
{
"id":"17hakg9a7jas",
"avatar": "bender.png",
"userName": "Bender",
"changeUsername": [Function changeUsername]
"changeAvatar": [Function changeAvatar]
}
*/c
使用这种模式,你可以实现函数式编程中的一个想法,称为柯里化。
柯里化
柯里化是一种设计模式(也是某些语言的特征),立即评估一个函数并返回第二个函数。
你可以使用闭包创建这些“柯里化”函数,定义并返回闭包的内部函数。
function multiply(a) {
return function (b) {
return function (c) {
return a * b * c
}
}
}
let mc1 = multiply(1);
let mc2 = mc1(2);
let res = mc2(3);
console.log(res);
let res2 = multiply(1)(2)(3);
console.log(res2);
这些 类型 采用 或 返回 一 一 一 一 一 函数 它 它 它 是 是 是 参数 参数 参数
let multiply = (a) => (b) => (c) => {
return a * b * c;
}
let mc1 = multiply(1);
let mc2 = mc1(2);
let res = mc2(3);
console.log(res);
let res2 = multiply(1)(2)(3);
console.log(res2);
Bạn có thể sử dụng nó để tạo ra các phần mềm hỗ trợ HTML cho các trang web của mình.
function createElement(element){
const el = document.createElement(element)
return function(content) {
return el.textNode = content
}
}
const bold = crearElement('b')
const italic = createElement('i')
const content="My content"
const myElement = bold(italic(content)) // <b><i>My content</i></b>
事件监听器
Bạn có thể sử dụng ứng dụng React để phát triển ứng dụng React.
假设你使用第三方库来呈现数据集合中的项目。这个库有一个名为 RenderItem
的组件,它只有一个可用的 prop,onClick
。这个 prop 不接收任何参数,也不返回值。
现在,在你的特定应用程序中,你要求当用户单击该项目时,该应用程序显示带有该时昊标馽玚 onClick
事件不接受参数——那么你能做什么呢?闭包可以发挥作用了:
// Closure
// with es5
function onItemClick(title) {
return function() {
alert("Clicked " + title)
}
}
// with es6
const onItemClick = title => () => alert(`Clcked ${title}`)
return (
<Container>
{items.map(item => {
return (
<RenderItem onClick={onItemClick(item.title)}>
<Title>{item.title}</Title>
</RenderItem>
)
})}
</Container>
)
在 这个 , , , , , , , , , , , , , , 另 一 个 个 , , , 满足
总结
你 甚至 不 正在 , , , , , , , , , , , , , , , , , 会 会 会 开启
闭包 是 开始 难以 的 你 , , , , , , , , , , , , , ,

🐦 在 Twitter 关注我 ✉️ 订阅邮件 ❤️ 支持我的工作
Nguồn: Cách sử dụng bao đóng trong JavaScript – Hướng dẫn cho người mới bắt đầu,Người viết: Matías Hernández