JavaScript 面试:何为闭包?

什么是闭包?

闭包即函数与其引用的周边状态(词法环境)绑定在一起形成的(封装)组合。换句话说,闭包可以让我们从函数内部访问其外部函数的作用域。在 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;
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() 的一种实现:

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);
});

可以看到,函数返回了一个保留了对 fixedArgs 访问的函数,而 fixedArgs 就是我们传给 partialApply() 的参数。


原作者:zhaofeihao
链接:https://juejin.im/post/5cfd11fbe51d4555fd20a30d

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值