實務上如何使用 Closure ?

栏目: JavaScript · 发布时间: 5年前

内容简介:Closure 是 ECMAScript 的代表性功能,也是 Functional Programming 的基礎,很多神妙的 FP 機制都是由 Closure 展開,善用 Closure 將使得程式碼更為精簡,可讀性更高,也更容易維護。ECMAScript 2015藉由 function 的 parameter 建立新的 function,而新的 function 則藉由 Closure 讀取原 function 的 parameter

Closure 是 ECMAScript 的代表性功能,也是 Functional Programming 的基礎,很多神妙的 FP 機制都是由 Closure 展開,善用 Closure 將使得程式碼更為精簡,可讀性更高,也更容易維護。

Version

ECMAScript 2015

Function Factory

藉由 function 的 parameter 建立新的 function,而新的 function 則藉由 Closure 讀取原 function 的 parameter

Higher Order Function

const data = [1, 2, 3];

console.log(data.map(x => x * 2));
console.log(data.map(x => x * 2 + 1));

ECMAScript 提供很多 Higher Order Function,如 Array.prototypesetTimeout() ,我們會傳入 Arrow Function。

若 Arrow Function 有重複,或者類似規則的 Arrow Function 不斷出現,則可藉由 Closure 抽出 Function Factory。

const data = [1, 2, 3];

const makeMapper = (a, b) => x => a * x + b;

console.log(data.map(makeMapper(2, 0)));
console.log(data.map(makeMapper(2, 1)));

抽出 makeMappr() ,藉由傳入不同 argument 給 makeMapper() 產生不同的 Arrow Fuction 給 map()

實務上如何使用 Closure ?

Object Method

const foo = {
  func1: x => 2 * x + 3,
  func2: x => 3 * x + 1,
};

console.log(foo.func1(1));
console.log(foo.func2(1));

當使用 Object Literal 定義 method 時,若 method 有重複,或者類似規則的 method 不斷出現,則可藉由 Closure 抽出 Function Factory。

const makeFunc = (a, b) => x => a * x + b;

const foo = {
  func1: makeFunc(2, 3),
  func2: makeFunc(3, 1),
};

console.log(foo.func1(1));
console.log(foo.func2(1));

func1()func2() 可藉由 makeFunc() 產生。

抽出 makeFunc() ,藉由傳入不同 argument 給 makeFunc() 產生不同的 method。

實務上如何使用 Closure ?

Factory Function 是是由 function 建立 object;而 Function Factory 是由 function 建立 function

Partial Application

Function 若只傳部分 argument 進去,則會回傳新的 function;直到所有 argument 都傳遞完全才會開始求值

const func = (x, y, z) => 2 * x + 3 * y + z;

console.log(func(10, 3, 2));
console.log(func(10, 1, 2));
console.log(func(10, 4, 2));

func 為多 argument 的 function,但實務上發現第 1 個 argument 都一樣,只有第 2 個與第 3 個 argument 在改變。

const func = x => (y, z) => 2 * x + 3 * y + z;

const func1 = func(10);

console.log(func1(3, 2));
console.log(func1(1, 2));
console.log(func1(4, 2));

將第 1 個 argument 傳入 func() 即可,會傳回新的 func1()

然後只需將第 2 個與第 3個 argument 傳入 func1() 即可得到結果,不需再重複傳入第一個 argument。

實務上如何使用 Closure ?

Currying

將原本 n 個 argument 的 function 改用 n 個 1 個 argument 的 function 所構成

const func = (x, y, z) => 2 * x + 3 * y + z;

console.log(func(1, 1, 1));
console.log(func(1, 1, 2));
console.log(func(1, 2, 2));
console.log(func(1, 2, 3));
console.log(func(1, 3, 4));

Partial Application 固然靈活,但只能針對特定 argument 組合回傳 function,若所有 funtion 都只有 1 個 argument,則 argument 的排列組合是最靈活的。

const func = x => y => z => 2 * x + 3 * y + z;

const func1 = func(1)(1);
const func2 = func(1)(2);

console.log(func1(1));
console.log(func1(2));
console.log(func2(2));
console.log(func2(3));
console.log(func(1)(3)(4));

無論需求怎麼變,都能任意湊出想要的 function。

實務上如何使用 Closure ?

Encapsulation

const foo = {
  name: 'Sam',
  sayHello: function() {
    return `Hello ${this.name}`;
  },
};

console.log(foo.sayHello());

使用 Object Literal 建立 object,所有的 property 都是 public 的,完全沒有任何 Encapsulation 可言,該如何如 OOP 有 private field 呢 ?

const foo = (function(name) {
  return {
    sayHello: () => `Hello ${name}`,
  }
}('Sam'));

console.log(foo.sayHello());

使用 Closure + IIFE, name 可如 OOP 的 constructor 般傳入設定初始值,重點是 name 被封裝在內部,外界無法更改。

實務上如何使用 Closure ?

const foo = (function(name) {
  const addTitle = name => `Mr. ${name}`;
  const title = addTitle(name);

  return {
    sayHello: () => `Hello ${title}`,
  }
}('Sam'));

console.log(foo.sayHello());

也能在 Anonymous Function 增加 variable 與 function,相當於 OOP 的 private field 與 private method。

實務上如何使用 Closure ?

Memorization

將耗時的運算結果存起來,若第二次執行該 function,則直接從 cache 讀出

const isPrime = value => {
  if (!isPrime.cache) isPrime.cache = {};
  if (isPrime.cache[value]) return isPrime.cache[value];

  let prime = value !== 1;
  for(let i = 2; i < value; i++) {
    if (value % 2 === 0) {
      prime = false;
      break;
    }
  }

  return isPrime.cache[value] = prime;
};

console.log(isPrime(13));

判斷質數 是很耗時的運算,我們希望能將運算過的結果 cache 下來,可直接對 isPrime() 新增 cache property,若 cache 有值則直接回傳,若 cache 無值才運算。

實務上如何使用 Closure ?

const isPrime = (() => {
  let cache = {};

  return value => {
    if (cache[value]) return cache[value];

    let prime = value !== 1;
    for(let i = 2; i < value; i++) {
      if (value % 2 === 0) {
        prime = false;
        break;
      }
    }

    return cache[value] = prime;
  };
})();

console.log(isPrime(13));

Memorization 另一種實現方式是利用 Closure,這種方式的優點在於 Encapsulation,cache 不會暴露在外被修改。

實務上如何使用 Closure ?

Conclusion

  • Closure 是 FP 的基礎,實務上最常使用的是 Function Factory
  • 透過 Closure + IIFE,也能以 function 實現 OOP 的 Encapsulation

以上所述就是小编给大家介绍的《實務上如何使用 Closure ?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

莱昂氏UNIX源代码分析

莱昂氏UNIX源代码分析

(澳)John Lions / 尤晋元 / 机械工业出版社 / 2000-7-1 / 49.00

本书由上、下两篇组成。上篇为UNIX版本6的源代码,下篇是莱昂先生对UNIX操作系统版本6源代码的详细分析。本书语言简洁、透彻,曾作为未公开出版物广泛流传了二十多年,是一部杰出经典之作。本书适合UNIX操作系统编程人员、大专院校师生学习参考使用。一起来看看 《莱昂氏UNIX源代码分析》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具