首先我們知道,js 當中是沒有函式過載的,在我們處理可變函式的引數的時候,需要使用一些小技巧。
arguments
arguments 是存在於函式(箭頭函式除外)中的一個內部變數。arguments 包含了傳遞給函式的引數的資訊。可以通過 arguments[0] 訪問函式的第一個引數,arguments[1]訪問函式的第二個函式,以此類推。即便如此,arguments 並不是一個陣列物件,它僅僅是隻能訪問索引和有 length 屬性。我們可以將 arguments 轉化為真正的陣列:
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);
var args = Array.from(arguments);
var args = [...arguments];
複製程式碼
arguments 會存在引擎的優化問題,在後續的操作技巧中,我們不會使用 arguments 物件,而使用 ES6 的語法代替。
不定引數
定義函式 function func([params][,...args])
。表示可以該函式可以接收不定長度的引數。
function func(...args) {
// 解構賦值
console.log(`arguments: ${args}`); // args 是陣列
let [params1, params2, ...paramsRest] = args; // 拿到傳入第一個引數 params1 和第二個引數 params2 和剩餘引數的陣列 paramsRest
}
複製程式碼
現在我們來實現一個函式,它會在內部呼叫另外一個函式。
const foo = (params1, params2, params3) => {
return params1 + params2 + params3;
};
// 我們不需要知道 foo 的形參列表。使用解構操作任意的形參列表。
const func = (...args) => {
foo(...args); // 函式呼叫時候展開
foo.call(null, ...args); // 使用 call 改變 this 的值。
foo.apply(null, args); // apply 可以直接接收引數陣列
};
複製程式碼
同理,部分引數確定,部分引數可變的寫法:
function func(params1, ...args) {
console.log(`arguments: ${args}`); // 剩餘引數的陣列
}
複製程式碼
按照 node.js 中的習慣,callback 一般是作為最後一個引數,如果中間引數是不定的話,此時需要通過 typeof 判斷:
// 判斷引數
function func(err, params1, params2, callback) {
if (typeof params1 === 'function') {
callback = params1;
params1 = null;
params2 = null;
} else if (typeof params2 === 'function') {
callback = params2;
params1 = null;
} else if (typeof callback !== 'function') {
throw new Error('引數錯誤');
}
}
// 一些小伎倆
function func(err, ...args) {
// 回撥函式是陣列 args 的最後一項
const callback = typeof args[args.length - 1] === 'function' ? args.pop() : null;
const [params1 = null, params2 = null] = args;
}
複製程式碼
原生 js 中,有些函式是能同時接收 引數列表或引數陣列作為引數的,比如說 concat。我們也可以利用 concat 的這一特性編寫這樣的函式:
const func = (...args) => {
const params = [].concat(...args); // 利用 concat 能同時接受多個引數或單個陣列的特性
console.log(params);
};
複製程式碼
預設引數
最基本的語法
function func(a, b = 0) {
return a + b;
}
func(5); // b 預設為 0
複製程式碼
結合物件解構和展開
function func(a, { opt1 = '1', opt2 = '2' }) {
console.log(a, opt1, opt2);
}
func(0, { opt1: '4' }); // 0 "4" "2"
func(0); // 錯誤! 因為第二引數沒定義
function func1(a, { opt1 = '1', opt2 = '2' } = {}) {
console.log(a, opt1, opt2);
}
func1(0); // 可以正確執行 0 "1" "2"
複製程式碼
也可以在函式內部進行處理
function func(a, opts) {
opts = Object.assign(
{
opt1: '1',
},
opts
); // 使用 Object.assign 賦予預設值
opts = {
opt1: '1',
...opts,
}; // 和 Object.assign 類似
const { opts1 = '1', opts2 = '2' } = opts; // 解構賦值,給予預設值。
}
複製程式碼
錯誤處理
結合預設引數的語法,我們可以實現一些錯誤檢測,比如說要求引數是必須傳入的:
function mandatory() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = mandatory()) {
return mustBeProvided;
}
foo(); // Error: Missing parameter
複製程式碼
檢查引數最大長度
function func(x, y, ...extra) {
if (extra.length > 0) {
throw new Error();
}
}
複製程式碼