Javascript 的函式與閉包
Function(函式)
function 的宣告
在 JavaScript 中有兩種宣告函式的方式,分別是使用傳統的 function 和 ES6 中新增的 Arrow function(箭頭函數),語法範例如下:
// normal function
function add(a, b) {
return a + b;
}
// arrow function
const add = (a, b) => {
return a + b;
};
Arrow Function
Arrow function 相較於傳統的函式宣告方式,在某些情境下具有簡化的優點:
簡潔的語法: 像前面出現的
add()
,如果箭頭函數只包含一個表達式,可以省略{}
和return
,這樣的寫法稱之為 implicit return(隱式返回)。// explicit return
const add = (a, b) => {
return a + b;
};
// implicit return
const add = (a, b) => a + b;返回 object literal: 使用 Arrow Function 除了可以更簡潔地返回變數或字串,有時候在處理物件時也需要返回 object literal 的形式。
傳統的寫法會是
function createPerson(name, age) {
return {
name: name,
age: age
};
}
const person = createPerson("John", 30);
console.log(person); // { name: 'John', age: 30 }使用箭頭函數的話可以寫成
// explicit return
const createPerson = (name, age) => ({
name: name,
age: age
});
// implicit return
const createPerson = (name, age) => ({ name, age });- 注意:返回 object literal 時,由於
{}
會是函數主體,所以需要將 object literal 包在()
中。
- 注意:返回 object literal 時,由於
Closure(閉包)
閉包是 JavaScript 中的一個重要概念,像前面的例子有提到使用函式時可以返回變數、字串、物件,那當然也可以返回函式,而當函式返回另一個函式時,就會涉及到 Closure 的概念。 (以下範例皆使用 ES6 的 Arrow Function 語法)
- 以下是 Closure 的簡單範例,可以看到
createHelloWorld()
這個函式會返回另一個函式。 - 可以把返回的函式 assign 給 f,接著使用
f()
就可以 print 出字串。
const createHelloWorld = () => {
return () => {
return "Hello World"
}
}
// 以上可以進一步簡化成
// const createHelloWorld = () => () => "Hello World";
const f = createHelloWorld();
console.log(f()); // "Hello World"
- Closure 有個特性是內部函數可以使用外部函數的變數,這個特性稱為 Capturing(捕獲)。
- 以下範例中內部函數能捕獲外部函數中的
counterValue
變數。
const createCounter = (n) => {
let counterValue = n;
return () => {
const currentValue = counterValue;
counterValue += 1;
return currentValue;
};
};
const counter = createCounter(10)
console.log(counter()) // 10
console.log(counter()) // 11
console.log(counter()) // 12
LeetCode 中的 Closure 題目
以下都是 Easy 難度的 JavaScript Closure 題目
2704. To Be Or Not To Be
Write a function expect
that helps developers test their code. It should take in any value val
and return an object with the following two functions.
toBe(val)
accepts another value and returnstrue
if the two values===
each other. If they are not equal, it should throw an error "Not Equal".notToBe(val)
accepts another value and returnstrue
if the two values!==
each other. If they are equal, it should throw an error "Equal".
解題:
根據前面有提過的宣告函式時回傳的方式,與基礎的 Closure 回傳函式的概念,可以先寫出程式的架構大致如下:
const expect = (val) => ({
toBe: (compareVal) => {
// if ...
},
notToBe: (compareVal) => {
// if ...
},
});
接著依據題目的敘述, toBe 和 notToBe 可以看作是用於檢查條件是否符合預期的斷言(assertions)。
const expect = (val) => ({
toBe: (compareVal) => {
if (val === compareVal) {
return true;
} else {
throw new Error("Not Equal");
}
},
notToBe: (compareVal) => {
if (val === compareVal) {
throw new Error("Equal");
} else {
return true;
}
}
})
console.log(expect(5).toBe(5)) // true
console.log(expect(5).notToBe(5)) // throws "Equal"
2665. Counter II
Write a function createCounter. It should accept an initial integer init. It should return an object with three functions.
The three functions are:
increment()
increases the current value by 1 and then returns it.decrement()
reduces the current value by 1 and then returns it.reset()
sets the current value to init and then returns it.
一樣可以先寫出程式的架構大致如下:
const createCounter = (init) => {
let currentValue = init;
return ({
increment: () => {
// increases the current value by 1 and then returns it
},
decrement: () => {
// reduces the current value by 1 and then returns it
},
reset: () => {
// sets the current value to init and then returns it
},
})
};
接著依據題目的敘述來補完
const createCounter = (init) => {
let currentValue = init;
return ({
increment: () => {
currentValue += 1
return currentValue
},
decrement: () => {
currentValue -= 1
return currentValue
},
reset: () => {
currentValue = init;
return currentValue
},
})
};
const counter = createCounter(5)
console.log(counter.increment()) // 6
console.log(counter.reset()) // 5
console.log(counter.decrement()) // 4