如何使用 ES6 參數和參數

已發表: 2022-03-10
快速總結 ↬開發人員越來越多地使用 ECMAScript 6 功能,很快這些功能將不可避免。 在本教程中,您將了解 ECMAScript 6 如何升級 JavaScript 中的參數處理等。

ECMAScript 6(或 ECMAScript 2015)是 ECMAScript 標準的最新版本,顯著改進了 JavaScript 中的參數處理。 我們現在可以使用休息參數、默認值和解構,以及其他新功能。

在本教程中,我們將詳細探討參數和參數,並了解 ECMAScript 6 如何升級它們。

參數與參數

參數和參數通常可以互換使用。 儘管如此,出於本教程的目的,我們將進行區分。 在大多數標準中,參數(或形式參數)是在函數聲明中給出的,而實參(或實際參數)是傳遞給函數的。 考慮這個函數:

 function foo(param1, param2) { // do something } foo(10, 20);

在這個函數中, param1param2是函數參數,傳遞給函數的值( 1020 )是參數。

擴展運算符 (…)

在 ECMAScript 5 中, apply()方法是一種方便的工具,可以將數組作為參數傳遞給函數。 例如,它通常與Math.max()方法一起用於查找數組中的最大值。 考慮這個代碼片段:

 var myArray = [5, 10, 50]; Math.max(myArray); // Error: NaN Math.max.apply(Math, myArray); // 50

Math.max()方法不支持數組; 它只接受數字。 將數組傳遞給Math.max()函數時,會引發錯誤。 但是當使用apply()方法時,數組作為單獨的數字發送,因此Math.max()方法可以處理它。

跳躍後更多! 繼續往下看↓

幸運的是,隨著 ECMAScript 6 中擴展運算符的引入,我們不再需要使用apply()方法。 使用擴展運算符,我們可以輕鬆地將表達式擴展為​​多個參數:

 var myArray = [5, 10, 50]; Math.max(...myArray); // 50

在這裡,展開運算符擴展myArray以為函數創建單獨的值。 雖然在 ECMAScript 5 中使用apply()模擬擴展運算符是可能的,但語法令人困惑並且缺乏擴展運算符的靈活性。 擴展運算符不僅更易於使用,而且包含更多功能。 例如,它可以多次使用,並且可以在function調用中與其他參數混合使用:

 function myFunction() { for(var i in arguments){ console.log(arguments[i]); } } var params = [10, 15]; myFunction(5, ...params, 20, ...[25]); // 5 10 15 20 25

擴展運算符的另一個優點是它可以很容易地與構造函數一起使用:

 new Date(...[2016, 5, 6]); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)

當然,我們可以在 ECMAScript 5 中重寫前面的代碼,但是我們需要使用複雜的模式來避免出現類型錯誤:

 new Date.apply(null, [2016, 4, 24]); // TypeError: Date.apply is not a constructor new (Function.prototype.bind.apply(Date, [null].concat([2016, 5, 6]))); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)

在函數調用中擴展運算符瀏覽器支持

桌面瀏覽器:

鉻合金火狐IE瀏覽器微軟邊緣歌劇蘋果瀏覽器
46 27 支持的 7.1

移動瀏覽器:

安卓版 Chrome 火狐手機Safari 移動版歌劇移動IE手機
46 27 8

休息參數

rest 參數與擴展運算符具有相同的語法,但它不是將數組擴展為參數,而是收集參數並將它們轉換為數組。

 function myFunction(...options) { return options; } myFunction('a', 'b', 'c'); // ["a", "b", "c"]

如果沒有參數,則其餘參數將設置為空數組:

 function myFunction(...options) { return options; } myFunction(); // []

在創建可變參數函數(接受可變數量參數的函數)時,rest 參數特別有用。 作為數組的好處,剩餘參數可以很容易地替換arguments對象(我們將在本教程後面解釋)。 考慮這個用 ECMAScript 5 編寫的函數:

 function checkSubstrings(string) { for (var i = 1; i < arguments.length; i++) { if (string.indexOf(arguments[i]) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true

此函數檢查字符串是否包含多個子字符串。 這個函數的第一個問題是我們必須在function體內部查看它是否有多個參數。 第二個問題是迭代必須從1而不是0開始,因為arguments[0]指向第一個參數。 如果我們稍後決定在字符串之前或之後添加另一個參數,我們可能會忘記更新循環。 使用其餘參數,我們很容易避免這些問題:

 function checkSubstrings(string, ...keys) { for (var key of keys) { if (string.indexOf(key) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true

此函數的輸出與前一個相同。 同樣,參數string填充了首先傳遞的參數,但其餘參數放入數組中並分配給變量keys

使用 rest 參數而不是arguments對象可以提高代碼的可讀性並避免 JavaScript 中的優化問題。 然而,其餘參數並非沒有限制。 例如,它必須是最後一個參數; 否則會出現語法錯誤:

 function logArguments(a, ...params, b) { console.log(a, params, b); } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter

另一個限制是function聲明中只允許有一個剩餘參數:

 function logArguments(...param1, ...param2) { } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter

休息參數瀏覽器支持

桌面瀏覽器:

鉻合金火狐IE瀏覽器微軟邊緣歌劇蘋果瀏覽器
47 15 支持的34

移動瀏覽器:

安卓版 Chrome 火狐手機Safari 移動版歌劇移動IE手機
47 15

默認參數

ECMAScript 5 中的默認參數

JavaScript 不支持 ECMAScript 5 中的默認參數,但有一個簡單的解決方法。 在函數內部使用邏輯OR運算符 ( || ),我們可以輕鬆模擬 ECMAScript 5 中的默認參數。考慮這個函數:

 function foo(param1, param2) { param1 = param1 || 10; param2 = param2 || 10; console.log(param1, param2); } foo(5, 5); // 5 5 foo(5); // 5 10 foo(); // 10 10

這個函數需要兩個參數,但是當它在沒有參數的情況下被調用時,它將使用默認值。 在函數內部,缺少的參數會自動設置為未定義; 因此,我們可以檢測這些參數並為它們聲明默認值。 為了檢測缺失的參數並設置默認值,我們使用邏輯OR運算符 ( || )。 這個操作符檢查它的第一個參數:如果它是真的,操作符返回它; 如果不是,則運算符返回其第二個參數。

這種方式在函數中很常用,但是有一個缺陷。 傳遞0null也會觸發默認值,因為這些被認為是虛假值。 因此,如果我們確實需要將0null傳遞給該函數,我們將需要另一種方法來檢查參數是否丟失:

 function foo(param1, param2) { if(param1 === undefined){ param1 = 10; } if(param2 === undefined){ param2 = 10; } console.log(param1, param2); } foo(0, null); // 0, null foo(); // 10, 10

在此函數中,檢查傳遞參數的類型以確保在分配默認值之前未定義它們。 這種方法只需要更多的代碼,但它是一種更安全的選擇,允許我們將0null傳遞給函數。

ECMAScript 6 中的默認參數

使用 ECMAScript 6,我們不再需要檢查未定義的值來模擬默認參數。 我們現在可以將默認值直接放在function聲明中:

 function foo(a = 10, b = 10) { console.log(a, b); } foo(5); // 5 10 foo(0, null); // 0 null

如您所見,省略參數會觸發默認值,但不會傳遞0null 。 我們甚至可以使用函數來檢索默認參數的值:

 function getParam() { alert("getParam was called"); return 3; } function multiply(param1, param2 = getParam()) { return param1 * param2; } multiply(2, 5); // 10 multiply(2); // 6 (also displays an alert dialog)

請注意,只有在省略第二個參數時才會調用getParam函數。 因此,當我們使用兩個參數調用multiply()函數時,不會顯示警報。

默認參數的另一個有趣特性是我們可以在function聲明中引用其他參數和變量:

 function myFunction(a=10, b=a) { console.log('a = ' + a + '; b = ' + b); } myFunction(); // a=10; b=10 myFunction(22); // a=22; b=22 myFunction(2, 4); // a=2; b=4

您甚至可以在function聲明中執行操作:

 function myFunction(a, b = ++a, c = a*b) { console.log(c); } myFunction(5); // 36

請注意,與其他一些語言不同,JavaScript 在調用時評估默認參數:

 function add(value, array = []) { array.push(value); return array; } add(5); // [5] add(6); // [6], not [5, 6]

默認參數瀏覽器支持

桌面瀏覽器:

特徵鉻合金火狐IE瀏覽器微軟邊緣歌劇蘋果瀏覽器
基本支持49 15 14
默認參數後沒有默認值的參數49 26 14

移動瀏覽器:

特徵安卓版 Chrome 火狐手機Safari 移動版歌劇移動IE手機
基本支持49 15
默認參數後沒有默認值的參數46 26

解構

解構是 ECMAScript 6 中的一個新特性,它使我們能夠從數組和對像中提取值,並使用類似於對象和數組字面量的語法將它們分配給變量。 語法清晰易懂,在將參數傳遞給函數時特別有用。

在 ECMAScript 5 中,配置對象通常用於處理大量可選參數,尤其是在屬性順序無關緊要的情況下。 考慮這個函數:

 function initiateTransfer(options) { var protocol = options.protocol, port = options.port, delay = options.delay, retries = options.retries, timeout = options.timeout, log = options.log; // code to initiate transfer } options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true }; initiateTransfer(options);

這種模式是 JavaScript 開發人員常用的,它運行良好,但我們必須查看function體內部,看看它需要什麼參數。 使用解構參數,我們可以在function聲明中清楚的指明參數:

 function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer }; var options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true } initiateTransfer(options);

在這個函數中,我們使用了對象解構模式,而不是配置對象。 這使我們的函數不僅更簡潔,而且更易於閱讀。

我們還可以將解構參數與常規參數組合:

 function initiateTransfer(param1, {protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer('some value', options);

請注意,如果在function調用中省略參數,將引發類型錯誤:

 function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer(); // TypeError: Cannot match against 'undefined' or 'null'

當我們需要參數時,這是所需的行為,但是如果我們希望它們是可選的呢? 為了防止參數丟失時出現此錯誤,我們需要為解構參數分配一個默認值:

 function initiateTransfer({protocol, port, delay, retries, timeout, log} = {}) { // code to initiate transfer } initiateTransfer(); // no error

在此函數中,提供了一個空對像作為解構參數的默認值。 現在,如果在沒有任何參數的情況下調用此函數,則不會發生錯誤。

我們還可以為每個解構參數分配一個默認值:

 function initiateTransfer({ protocol = 'http', port = 800, delay = 150, retries = 10, timeout = 500, log = true }) { // code to initiate transfer }

在此示例中,每個屬性都有一個默認參數,無需我們手動檢查未定義的參數並在function體內分配默認值。

解構瀏覽器支持

桌面瀏覽器:

特徵鉻合金火狐IE瀏覽器微軟邊緣歌劇蘋果瀏覽器
基本支持49 2.0 14 7.1
具有默認值分配的解構參數49 47 14

移動瀏覽器:

特徵安卓版 Chrome 火狐手機Safari 移動版歌劇移動IE手機
基本支持49 1 8
默認參數後沒有默認值的參數49 47

傳遞參數

將參數傳遞給函數有兩種方法:通過引用或通過值。 修改通過引用傳遞的參數會全局反映,但修改通過值傳遞的參數僅反映在函數內部。

在某些語言中,例如 Visual Basic 和 PowerShell,我們可以選擇指定是按引用還是按值傳遞參數,但 JavaScript 並非如此。

按值傳遞參數

從技術上講,JavaScript 只能按值傳遞。 當我們通過值將參數傳遞給函數時,會在function範圍內創建該值的副本。 因此,對值的任何更改僅反映在function內部。 考慮這個例子:

 var a = 5; function increment(a) { a = ++a; console.log(a); } increment(a); // 6 console.log(a); // 5

在這裡,修改函數內部的參數對原始值沒有影響。 因此,當從函數外部記錄變量時,打印的值仍然是5

通過引用傳遞參數

在 JavaScript 中,一切都是按值傳遞的,但是當我們傳遞一個引用對象(包括數組)的變量時,“值”是對該對象的引用,並且更改變量引用的對象的屬性確實會改變底層對象。

考慮這個函數:

 function foo(param){ param.bar = 'new value'; } obj = { bar : 'value' } console.log(obj.bar); // value foo(obj); console.log(obj.bar); // new value

如您所見,對象的屬性在函數內部被修改,但修改後的值在函數外部可見。

當我們傳遞諸如數組或對象之類的非原始值時,會在後台創建一個變量,該變量指向內存中原始對象的位置。 然後將此變量傳遞給函數,修改它會影響原始對象。

類型檢查和缺少或額外的參數

在強類型語言中,我們必須在function聲明中指定參數的類型,但是 JavaScript 缺少這個特性。 在 JavaScript 中,我們傳遞給函數的數據類型或參數數量並不重要。

假設我們有一個只接受一個參數的函數。 當我們調用該函數時,我們不僅限於將一個參數傳遞給函數; 我們可以自由地傳遞一個、兩個或更多參數! 我們甚至可以選擇什麼都不通過,這樣就不會發生錯誤。

自變量和參數的數量可以通過兩種方式有所不同:

  • 參數少於參數
    缺少的參數將等於undefined
  • 參數多於參數
    額外的參數將被忽略,但可以通過特殊的類似數組的變量參數檢索(接下來討論)。

強制參數

如果function調用中缺少參數,它將被設置為undefined 。 如果省略參數,我們可以利用這種行為並拋出錯誤:

 function foo(mandatory, optional) { if (mandatory === undefined) { throw new Error('Missing parameter: mandatory'); } }

在 ECMAScript 6 中,我們可以更進一步,使用默認參數來設置強制參數:

 function throwError() { throw new Error('Missing parameter'); } function foo(param1 = throwError(), param2 = throwError()) { // do something } foo(10, 20); // ok foo(10); // Error: missing parameter

參數對象

為了替換arguments對象,ECMAScript 4 添加了對 rest 參數的支持,但 ECMAScript 4 從未實現。 隨著 ECMAScript 6 的發布,JavaScript 現在正式支持其餘參數。 它還取消了放棄對arguments對象的支持的計劃。

arguments對像是一個在所有函數中都可用的類數組對象。 它允許通過數字而不是名稱檢索傳遞給函數的argument值。 該對象允許我們將任意數量的參數傳遞給函數。 考慮以下代碼片段:

 function checkParams(param1) { console.log(param1); // 2 console.log(arguments[0], arguments[1]); // 2 3 console.log(param1 + arguments[0]); // 2 + 2 } checkParams(2, 3);

此函數預計只接收一個參數。 當我們用兩個參數調用它時,第一個參數可以通過參數名稱param1或參數對象arguments[0]在函數中訪問,但第二個參數只能作為arguments[1]訪問。 另外,請注意arguments對象可以與命名參數一起使用。

arguments對象包含傳遞給函數的每個參數的條目,第一個條目的索引從0開始。 如果我們想在上面的示例中訪問更多參數,我們將編寫arguments[2]arguments[3]等等。

我們甚至可以完全跳過設置命名參數而只使用arguments對象:

 function checkParams() { console.log(arguments[1], arguments[0], arguments[2]); } checkParams(2, 4, 6); // 4 2 6

事實上,命名參數是一種方便,而不是必需品。 同樣,其餘參數可用於反映傳遞的參數:

 function checkParams(...params) { console.log(params[1], params[0], params[2]); // 4 2 6 console.log(arguments[1], arguments[0], arguments[2]); // 4 2 6 } checkParams(2, 4, 6);

arguments對像是一個類似數組的對象,但它缺少諸如slice()foreach()之類的數組方法。 為了在arguments對像上使用數組方法,首先需要將對象轉換為真正的數組:

 function sort() { var a = Array.prototype.slice.call(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]

在此函數中, Array.prototype.slice.call()用作將arguments對象轉換為數組的快速方法。 接下來, sort()方法對數組中的項進行排序並返回它。

ECMAScript 6 有一個更直接的方法。 Array.from()是 ECMAScript 6 中的新增功能,它從任何類似數組的對象創建一個新數組:

 function sort() { var a = Array.from(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]

長度屬性

儘管參數對像在技術上不是一個數組,但它有一個length屬性,可用於檢查傳遞給函數的參數數量:

 function countArguments() { console.log(arguments.length); } countArguments(); // 0 countArguments(10, null, "string"); // 3

通過使用length屬性,我們可以更好地控制傳遞給函數的參數數量。 例如,如果一個函數需要兩個參數才能工作,我們可以使用length屬性來檢查傳遞的參數的數量,如果它們少於預期,則拋出錯誤:

 function foo(param1, param2) { if (arguments.length < 2) { throw new Error("This function expects at least two arguments"); } else if (arguments.length === 2) { // do something } }

其餘參數是數組,因此它們具有length屬性。 在 ECMAScript 6 中,前面的代碼可以用剩餘參數重寫:

 function foo(...params) { if (params.length < 2) { throw new Error("This function expects at least two arguments"); } else if (params.length === 2) { // do something } }

Callee 和 Caller 屬性

callee屬性是指當前正在運行的函數, caller是指調用了當前正在執行的函數的函數。 在 ECMAScript 5 嚴格模式下,這些屬性已被棄用,嘗試訪問它們會導致 TypeError。

arguments.callee屬性在遞歸函數中很有用(遞歸函數是通過名稱引用自身的常規函數),尤其是在函數名不可用時(匿名函數)。 因為匿名函數沒有名稱,所以引用它的唯一方法是通過arguments.callee

 var result = (function(n) { if (n <= 1) { return 1; } else { return n * arguments.callee(n - 1); } })(4); // 24

嚴格模式和非嚴格模式中的參數對象

在 ECMAScript 5 非嚴格模式下, arguments對像有一個不尋常的特性:它保持其值與相應命名參數的值同步。

考慮以下代碼片段:

 function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // true return param } foo(200); // 500

在此函數內部,為arguments[0]分配了一個新值。 因為arguments的值始終與命名參數的值保持同步,所以對arguments[0]的更改也會更改param的值。 實際上,它們就像同一個變量的兩個不同名稱。 在 ECMAScript 5 嚴格模式下, arguments對象的這種令人困惑的行為已被刪除:

 "use strict"; function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // false return param } foo(200); // 200

這一次,更改arguments[0]不會影響param ,並且輸出符合預期。 該函數在 ECMAScript 6 中的輸出與在 ECMAScript 5 嚴格模式中相同,但請記住,在function聲明中使用默認值時, arguments對像不受影響:

 function foo(param1, param2 = 10, param3 = 20) { console.log(param1 === arguments[0]); // true console.log(param2 === arguments[1]); // true console.log(param3 === arguments[2]); // false console.log(arguments[2]); // undefined console.log(param3); // 20 } foo('string1', 'string2');

在這個函數中,即使param3有一個默認值,它也不等於arguments[2] ,因為只有兩個參數被傳遞給函數。 換句話說,設置默認值對arguments對像沒有影響。

結論

ECMAScript 6 為 JavaScript 帶來了數百個大大小小的改進。 越來越多的開發人員正在使用 ECMAScript 6 的特性,很快這些特性將不可避免。 在本教程中,我們了解了 ECMAScript 6 如何升級 JavaScript 中的參數處理,但我們只是觸及了 ECMAScript 6 的皮毛。該語言的許多其他新的和有趣的特性值得一試。

鏈接

  • ECMAScript 6 兼容性表,Juriy Zaytsev
  • “ECMAScript 2015 語言規範”,ECMA 國際