闭包很重要,因为它们控制特定函数在作用域中的作用和作用,以及在同一包含作用域中的兄弟功能之间共享变量。了解变量和函数如何相互关联对于了解功能和面向对象的编程风格中代码中发生的事情至关重要。
在面试中遗漏这个问题如此不利的原因是,对闭包如何工作的误解是一个非常明显的危险信号,它可能表明缺乏深厚的经验,不仅在JavaScript中,而且在任何依赖闭包的语言中( Haskell,C#,Python等…)。
在JavaScript中进行编码而不了解闭包就如同在不理解语法规则的情况下讲英语一样,尽管您可能会理解自己的想法,但可能会有些尴尬。
当您试图了解别人写的内容时,您也容易遭受误解。
您不仅应该知道闭包是什么,还应该知道它为什么重要,并且能够轻松回答闭包的几种可能的用例。
闭包经常在JavaScript中用于对象数据隐私,事件处理程序和回调函数以及部分应用程序,currying和其他功能编程模式中。
准备进行快速跟进:“您能说出闭合的两种常见用法吗?”
什么是闭包?
闭合是捆绑在一起的函数的组合(封闭)与到其周围的状态的引用(在词法环境)。换句话说,闭包使您可以从内部函数访问外部函数的范围。在JavaScript中,每次创建函数时都会在函数创建时创建闭包。
要使用闭包,请在另一个函数中定义一个函数并将其公开。要公开一个函数,请将其返回或传递给另一个函数。
即使在返回外部函数之后,内部函数也可以访问外部函数范围内的变量。
使用闭包(示例)
除其他事项外,闭包通常用于为对象提供数据隐私。数据隐私是一项重要属性,可帮助我们对接口进行编程,而不是对实现进行编程。这是一个重要的概念,可帮助我们构建更强大的软件,因为与接口合同相比,实现细节更可能以破坏性的方式发生变化。
“编程是面向接口,而不是实现。”
设计模式:可重用的面向对象软件的元素
在JavaScript中,闭包是用于实现数据隐私的主要机制。当使用闭包进行数据保密时,包含的变量仅在包含(外部)函数的范围内。除了通过对象的特权方法之外,您无法从外部范围获取数据。在JavaScript中,闭包范围内定义的任何公开方法都具有特权。例如:
const getSecret = (secret) => {
return {
get: () => secret
};
};
test('Closure for object privacy.', assert => {
const msg = '.get() should have access to the closure.';
const expected = 1;
const obj = getSecret(1);
const actual = obj.get();
try {
assert.ok(secret, 'This throws an error.');
} catch (e) {
assert.ok(true, `The secret var is only available
to privileged methods.`);
}
assert.equal(actual, expected, msg);
assert.end();
});
在上面的示例中,.get()方法在getSecret() 范围内定义,这使它可以访问getSecret() 中的任何变量,并使其成为特权方法。在这种情况下,参数为secret
。
对象不是产生数据隐私的唯一方法。闭包也可以用于创建有状态函数, 其返回值可能会受其内部状态影响,例如:
const secret = msg => () => msg;
// Secret - creates closures with secret messages.
// https://gist.github.com/ericelliott/f6a87bc41de31562d0f9
// https://jsbin.com/hitusu/edit?html,js,output
// secret(msg: String) => getSecret() => msg: String
const secret = (msg) => () => msg;
test('secret', assert => {
const msg = 'secret() should return a function that returns the passed secret.';
const theSecret = 'Closures are easy.';
const mySecret = secret(theSecret);
const actual = mySecret();
const expected = theSecret;
assert.equal(actual, expected, msg);
assert.end();
});
在函数式编程中,闭包经常用于部分应用和计算。这需要一些定义:
应用: 是应用一个函数到它的参数的过程,以产生一个返回值。
部分应用: 将函数应用于其某些参数的过程。返回部分应用的函数以供以后使用,部分应用程序修复(部分地将函数应用于)返回的函数中的一个或多个参数,并且返回的函数将其余参数用作参数,以完成函数应用程序。
部分应用程序利用闭包范围来固定参数。您可以编写将部分参数应用于目标函数的通用函数。它将具有签名的特性:
partialApply(targetFunction: Function, ...fixedArgs: Any[]) =>
functionWithFewerParams(...remainingArgs: Any[])
它将接受一个函数,该函数接受任意数量的参数,然后是我们要部分应用于该函数的参数,然后返回一个将保留其余参数的函数。
一个例子会有所帮助。假设您有一个将两个数字相加的函数:
const add = (a, b) => a + b;
现在,您需要一个将10加到任意数字的函数。我们将其称为add10()
。add10(5)的结果应该是15。我们的partialApply()
函数可以实现:
const add10 = partialApply(add, 10);
add10(5);
在此示例中,参数“ 10” 成为在add10()闭包范围内记住的固定参数。
让我们看一下可能的partialApply()
实现:
// Generic Partial Application Function
// https://jsbin.com/biyupu/edit?html,js,output
// https://gist.github.com/ericelliott/f0a8fd662111ea2f569e
// partialApply(targetFunction: Function, ...fixedArgs: Any[]) =>
// functionWithFewerParams(...remainingArgs: Any[])
const partialApply = (fn, ...fixedArgs) => {
return function (...remainingArgs) {
return fn.apply(this, fixedArgs.concat(remainingArgs));
};
};
test('add10', assert => {
const msg = 'partialApply() should partially apply functions'
const add = (a, b) => a + b;
const add10 = partialApply(add, 10);
const actual = add10(5);
const expected = 15;
assert.equal(actual, expected, msg);
});
如您所见,它仅返回一个函数,该函数保留对传递给partialApply()函数的fixedArgs参数的访问。
网友评论