跳至主要内容

[JS] JavaScript 函式(Function)

函式的所有方法 @ MDN

箭頭函式(arrow function, lambda function)

基本用法

// 基本寫法,沒有參數的時候要記得加上空括號
let greeting = () => {
return 'Hello';
};

// 如果只有一行,可以省略{ }
let greeting = () => console.log('Hello');

// 如果只是要回傳某個值,可以省略 { } 和 return,但是需要在一行內寫完。
let greeting = () => 'Hello';

// 如果要直接回傳某個物件,要多用 () 包起來
let greeting = ({ name }) => ({ name });

// 當函數只有一個參數時,不需要使用括號
let greeting = (person) => 'Hello ' + person;

// 兩個以上的參數,需要使用括號
let add = (a, b) => a + b;

對 this 的影響

在箭頭函數中,this 指稱的對象在所定義時就固定了,this 會和 instance object 綁定,而不會隨著使用時的脈絡而改變(An arrow function is bound to its parent scope):

// 傳統函式寫法 this 會隨著呼叫的脈絡而改變。
// 使用箭頭函式時,不論什麼時候執行函式,this 會和 instance object 綁定。

var Person = function (data) {
for (var key in data) {
this[key] = data[key];
}
this.getKeys = function () {
return Object.keys(this);
};
};

var Alena = new Person({ name: 'Alena', role: 'Teacher' });

console.log(Alena.getKeys()); // 'this' refers to 'Alena'

var getKeys = Alena.getKeys;
console.log(getKeys()); // 'this' refers to the node process

函式預設值(function default value)

基本用法

function add(x = 3, y = 5) {
console.log(x + y);
}

add(); // 8

觀念

丟入的參數值會由前往後代入:

// 只給後面的變數預設值,則可以代入一個參數就好
let add_with_last_para = (a, b = 3) => a + b;
add_with_last_para(2); // 5
add_with_last_para(5, 2); // 7

// 只給前面的變數預設值,後面沒代入的參數會變 undefined
// 如果第一個參數想要代入預設值,可以用 undefined
let add_with_first_para = (a = 3, b) => a + b;
console.log(add_with_first_para(2)); // NaN (a = 2, b = undefined)
console.log(add_with_first_para(undefined, 4)); // 7

因此把需要預設值的參數放在最後一個

/* Always put default parameters last. @ airbnb 7.9 */
// bad
function handleThings(opts = {}, name) {...}

// good
function handleThings(name, opts = {}) {...}

若帶入的參數為物件,則可參考以物件為參數帶入函式中

不要改變傳入的參數

不要試圖改變函式參數的值(例如,使用傳統賦予函數預設值的方式),這是有副作用的(side-effect)、可能會導致無預期的 bug ,並且降低效能:

Use default parameter syntax rather than mutating function arguments. @airbnb 7.7

// really bad
function handleThings(opts) {
opts = opts || {}
opts = 'a'
if (!a) { a = 1; }
// ...
}

// good
function handleThings(opts = {}) { ... }
function f3 (a) {
const b = a || 1;
}

Never mutate parameters @ airbnb 7.12

// bad
function f1(obj) {
obj.key = 1;
}

函式的常用方法

Function.prototype.call()

/**
* fun.call(thisArg[, arg1[, arg2[, ...]]])
* 你可以在呼叫一個現存的函數時,使用不一樣的 this 物件。 this 會參照到目前的物件。
* 不同處只有 call() 接受一連串的參數,而 apply() 單一的array作為參數
**/

fn.call(<thisArg>, <arg1>, <arg2>);

取得函式所有的參數

使用展開語法(spread syntax)

keywords: get all parameters value in function
  • 不要使用 arguments 作為參數名稱,arguments 是保留字(@airbnb 7.5)
  • 如果需要取得所有輸入的參數內容,使用 ... rest syntax,因為透過 ... 取得的是真實的陣列:
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args;
}

// good
function concatenateAll(...args) {
// args 會是一個陣列
return args;
}

concatenateAll('car', 54, 'tree'); // [ 'car', 54, 'tree' ]

以物件為參數帶入函式中

方法一:使用物件的解構賦值

let person = {
name: 'Anna',
age: 56,
job: { company: 'Tesco', title: 'Manager' },
};

function greetWithSpreadOperator({ age, name, job: { company, title } }) {
var yearOfBirth = 2018 - age;
console.log(`${name} works at ${company} as ${title} and was born in ${yearOfBirth}.`);
}

greetWithSpreadOperator(person); // Anna works at Tesco as Manager and was born in 1962.

可以帶入預設值

let person = {
name: 'Aaron',
age: 29,
};

function greet({ name, age, interestLevel = 5 } = {}) {
console.log(`Your name is ${name}, and age is ${age}. Your interest level is ${interestLevel}.`);
}

greet(person); // Your name is Aaron, and age is 29. Your interest level is 5.
greet(); // 如果沒有在參數的地方使用 = {},則當沒有帶入任何東西時會直接爆掉

方法三:在函式中取出參數

// 方法三: 在函式中取出參數(較舊的方法)
let person = {
name: 'Anna',
age: 56,
job: { company: 'Tesco', title: 'Manager' },
};

greetWithObjectAsOption(person);

function greetWithObjectAsOption(person) {
let name = person.name;
let age = person.age;
let job = person.job;
console.log(
`Your name is ${name}, and your age is ${age}. You work in ${job.company} as ${job.title}.`,
);
}

Code Style

定義函式:使用 function declaration

  • 由於 Function declarations 具有變數提升(hoisted)的特性,因此建議在定義變數的時候使用 Function expression(@ airbnb 7.1):
// bad
function foo () {...}

// bad
const foo = function () {...}

// good: 前面定義使用時的函式名稱(簡短),後面的名稱可描述此函式的作用
const short = function longUniqueMoreDescriptiveLexicalFoo () {...};
short(); // call the function
  • 風格:
// bad
const f = function () {};
const g = function () {};
const h = function () {};

// good
const x = function () {};
const y = function a() {};

使用 IIFE

@ airbnb 7.2

// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
})();

不要在 non-function block 中定義函式

@ airbnb 7.3, 7.4

不要在 if, while, 等等非函式的區塊中定義函式,這可能導致問題。如果有在其中定義函式的需要,則先定義一個變數,再把函式給它:

// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}

// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}

匿名函式建議使用 arrow function

@airbnb 8.1

// good
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});

參考

函式的所有方法 @ MDN