Leetcode 30 天 JavaScript 挑战 - 01. 闭包

106 阅读4分钟

2667. 创建 Hello World 函数

请你编写一个名为 createHelloWorld 的函数。它应该返回一个新的函数,该函数总是返回 "Hello World" 。

示例 1:

输入:args = []
输出:"Hello World"
解释:
const f = createHelloWorld();
f(); // "Hello World"

createHelloWorld 返回的函数应始终返回 "Hello World"。

示例 2:

输入:args = [{},null,42]
输出:"Hello World"
解释:
const f = createHelloWorld();
f({}, null, 42); // "Hello World"

可以传递任何参数给函数,但它应始终返回 "Hello World"

提示:

  • 0 <= args.length <= 10

思路

送分题。以下为注意点:

  1. 因为需要返回一个函数,所以 createHelloWorld 的返回值是函数;
  2. 使用剩余参数,才可以传入任意数量的参数;
  3. 不管剩余参数传入的参数是什么,直接固定的 'Hello World' 即可。

这题也可以用箭头函数,但题目模板是函数语法,所以略。

代码

function createHelloWorld() {
  return function (...args: any[]): string {
    return 'Hello World';
  };
};

/**
 * const f = createHelloWorld();
 * f(); // "Hello World"
 */

2620. 计数器

给定一个整型参数 n,请你编写并返回一个 counter 函数。这个 counter 函数最初返回 n,每次调用它时会返回前一个值加 1 的值 ( n ,  n + 1 ,  n + 2 ,等等)。

示例 1:

输入:
n = 10 
["call","call","call"]
输出:[10,11,12]
解释:
counter() = 10 // 第一次调用 counter(),返回 n。
counter() = 11 // 返回上次调用的值加 1。
counter() = 12 // 返回上次调用的值加 1。

示例 2:

输入:
n = -2
["call","call","call","call","call"]
输出:[-2,-1,0,1,2]
解释:counter() 最初返回 -2。然后在每个后续调用后增加 1

提示:

  • -1000 <= n <= 1000
  • 0 <= calls.length <= 1000
  • calls[i] === "call"

思路

这个问题旨在介绍 闭包(closures) 的概念。在 JavaScript 中,函数具有对在相同作用域以及任何外部作用域中声明的所有变量的引用。这些作用域被称为函数的 词法环境。函数与其环境的组合被称为 闭包。

在 JavaScript 中,你可以在其他函数内部声明并返回函数。内部函数可以访问在其上方声明的任何变量。

所以创建了函数后进行调用时候,调用的时候使用的是同一个变量。

代码

function createCounter(n: number): () => number {
  return function () {
    return n++;
  }
}


/** 
 * const counter = createCounter(10)
 * counter() // 10
 * counter() // 11
 * counter() // 12
 */

2704. 相等还是不相等

请你编写一个名为 expect 的函数,用于帮助开发人员测试他们的代码。它应该接受任何值 val 并返回一个包含以下两个函数的对象。

  • toBe(val) 接受另一个值并在两个值相等( === )时返回 true 。如果它们不相等,则应抛出错误 "Not Equal" 。
  • notToBe(val) 接受另一个值并在两个值不相等( !== )时返回 true 。如果它们相等,则应抛出错误 "Equal" 。

示例 1:

输入:func = () => expect(5).toBe(5)
输出:{"value": true}
解释:5 === 5 因此该表达式返回 true

示例 2:

输入:func = () => expect(5).toBe(null)
输出:{"error": "Not Equal"}
解释:5 !== null 因此抛出错误 "Not Equal".

示例 3:

输入:func = () => expect(5).notToBe(null)
输出:{"value": true}
解释:5 !== null 因此该表达式返回 true.

思路

这题看起来比较高级,其实就是需要一个对象工厂函数。当执行 expect 函数之后,会返回一个包含两个方法的对象:toBe和 notToBe。其他无难度。

代码

type ToBeOrNotToBe = {
  toBe: (val: any) => boolean;
  notToBe: (val: any) => boolean;
};

function expect(val: any): ToBeOrNotToBe {
  // 执行 expect 后返回一个存储函数的对象
  return {
    toBe: (val1: any) => {
      if (val !== val1) {
        throw new Error('Not Equal');
      }
      return true;
    },
    notToBe: (val2: any) => {
      if (val === val2) {
        throw new Error('Equal');
      }
      return true;
    },
  };
}

/**
 * expect(5).toBe(5); // true
 * expect(5).notToBe(5); // throws "Equal"
 */

2665. 计数器 II

请你写一个函数 createCounter。这个函数接收一个初始的整数值 init。并返回一个包含三个函数的对象。

这三个函数是:

  • increment() 将当前值加 1 并返回。
  • decrement() 将当前值减 1 并返回。
  • reset() 将当前值设置为 init 并返回。

示例 1:

输入:init = 5, calls = ["increment","reset","decrement"]
输出:[6,5,4]
解释:
const counter = createCounter(5);
counter.increment(); // 6
counter.reset(); // 5
counter.decrement(); // 4

示例 2:

输入:init = 0, calls = ["increment","increment","decrement","reset","reset"]
输出:[1,2,1,0,0]
解释:
const counter = createCounter(0);
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1
counter.reset(); // 0
counter.reset(); // 0

思路

因为还有 reset 函数存在,需要重置数字到初始值,所以 init 不能修改(而是用来记录初始值),并重新声明一个变量做闭包,用于数字的计算。

代码

type Counter = {
  increment: () => number;
  decrement: () => number;
  reset: () => number;
};

function createCounter(init: number): Counter {
  let num = init;
  return {
    increment: () => ++num,
    decrement: () => --num,
    reset: () => {
      num = init;
      return num;
    },
  };
}

/**
 * const counter = createCounter(5)
 * counter.increment(); // 6
 * counter.reset(); // 5
 * counter.decrement(); // 4
 */