摘要:我們可以利用這一點,如果參數(shù)缺失就拋出錯誤在中,我們可以更近一步,使用默認參數(shù)來設(shè)置強制參數(shù)對象在的時候默認參數(shù)就被加入,來代替對象,但并沒有實現(xiàn)。隨著的發(fā)布,現(xiàn)在官方支持了默認參數(shù)。
ECMAScript 6(或者叫 ECMAScript 2015)是 ECMAScript 的最新標準,極大的提高了 JavaScript 中處理參數(shù)的能力。現(xiàn)在我們可以使用 rest 參數(shù)(rest parameters)、默認值(default values)和解構(gòu)(destructuring)以及其他許多新的特性。本文我們將探索參數(shù)(arguments)和參數(shù)(parameter)的方方面面,看一下ES6是如何對他們改進和提升的。
Arguments 和 Parametersarguments 和 Parameters 的含義通常是可以互換的。盡管如此,為了本文的目標,還是要做出區(qū)分。在大多數(shù)的標準中,函數(shù)聲明時給出的叫做 parameters(或者叫 formal parameters),而傳遞給函數(shù)的叫做的 arguments(或者叫 actual arguments),看下面的函數(shù):
function foo(param1, param2) { // do something } foo(10, 20);
在這個函數(shù)中,param1 和 param2 是函數(shù)的 parameters,而傳遞給函數(shù)的值(10 和 20)是 arguments。
譯者注:本文后面不再區(qū)分 arguments 和 parameters,統(tǒng)一譯作參數(shù)。
擴展運算符(...)在 ES5 中,apply() 方法可以很方便將數(shù)組作為參數(shù)傳遞給函數(shù),經(jīng)常用于使用 Math.max() 來取得數(shù)組的最大值。看下面的代碼段:
var myArray = [5, 10, 50]; Math.max(myArray); // Error: NaN Math.max.apply(Math, myArray); // 50
Math.max() 方法不支持數(shù)組,只接受數(shù)字作為參數(shù)。當數(shù)組傳遞給函數(shù),函數(shù)會拋出錯誤。但是當使用 apply() 方法后,數(shù)組變成了一個個多帶帶的數(shù)組傳遞給了函數(shù),所以 Math.max() 就能夠正確的執(zhí)行了。
幸運的是,ES6 給我們帶來了擴展運算符,我們就不必再繼續(xù)使用 apply() 方法了。我們可以將表達式輕松的展開為多個參數(shù)。
var myArray = [5, 10, 50]; Math.max(...myArray); // 50
在這里我們通過擴展運算符將 myArray 展開成了一個個多帶帶的值。雖然 ES5 中我們可以通過 apply() 方法來模擬擴展運算符,但是語法上讓人迷惑,并且缺少可擴展性。擴展運算符不僅易于使用,還帶來了許多新的特性。比如,你可以在函數(shù)調(diào)用時多次使用擴展運算符,并且還可以和其他參數(shù)混合在一起。
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
擴展運算符另一大好處就是他可以很容易的和構(gòu)造函數(shù)(constructor)一起使用:
new Date(...[2016, 5, 6]); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)
當前我們可以使用 ES5 來重寫上面的代碼,不過我們需要一個復雜的方法來避免一個類型錯誤:
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)REST 參數(shù)
rest 參數(shù)和擴展運算符是一樣的語法,但是他不是將數(shù)組展開成一個個的參數(shù),而是將一個個參數(shù)轉(zhuǎn)換為數(shù)組。
譯者注:rest 參數(shù)和擴展運算符雖然一樣的語法,在這里你就可以看出作者強調(diào)的 arguments 和 parameters 的區(qū)別了。擴展運算符用于函數(shù)調(diào)用的參數(shù)(arguments)中,而 rest 參數(shù)用于函數(shù)聲明的參數(shù)(parameters)中。
function myFunction(...options) { return options; } myFunction("a", "b", "c"); // ["a", "b", "c"]
如果沒有提供參數(shù),rest 參數(shù)會被設(shè)置為空數(shù)組:
function myFunction(...options) { return options; } myFunction(); // []
當創(chuàng)建可見函數(shù)(接受數(shù)量可變的參數(shù)的函數(shù))的時候,rest 參數(shù)就顯得十分有用。因為 rest 參數(shù)是一個數(shù)組,所以可以很方便的替換 arguments 對象(將會在下文討論)。看下面一個使用 ES5 編寫的方法:
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
這個函數(shù)的作用是檢查一個字符串是否包含指定的一系列字符串。這個函數(shù)的第一個問題就是,我們必須查看函數(shù)體才知道函數(shù)接受多個參數(shù)。另外 arguments 的迭代必須從 1 開始,因為 arguments[0] 是第一個參數(shù)。如果我們稍后給第一參數(shù)之后再添加參數(shù),或許我們就忘記更新這個循環(huán)了。使用 rest 參數(shù),我們可以很輕易的避開這個問題:
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
函數(shù)的輸出和上一個函數(shù)一樣。再重復一次,string 參數(shù)作為第一個參數(shù)傳入,剩下的參數(shù)被塞進一個數(shù)組并且賦值給了變量 keys。
使用 rest 參數(shù)代替 arguments 不僅提高了代碼的可讀性,并且避免了 JavaScript 中的性能問題。盡管如此,rest 參數(shù)并不能無限制使用,舉個例子,它只能是最后一個參數(shù),否則會導致語法錯誤。
function logArguments(a, ...params, b) { console.log(a, params, b); } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter
另一個限制方法聲明時只允許一個 rest 參數(shù):
function logArguments(...param1, ...param2) { } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter默認值 ES5 中的默認參數(shù)
ES5 中 JavaScript 并不支持默認值,但這里有個很簡單的實現(xiàn),使用 OR
運算符(||),我們可以很容易的模擬默認參數(shù),看下面的代碼:
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
這個函數(shù)期望接收兩個參數(shù),但當無參數(shù)調(diào)用時,它會使用默認值。在函數(shù)內(nèi),缺失的參數(shù)自動設(shè)置為 undefined,所以我們檢查這些參數(shù),并給他們設(shè)置默認值。為了檢測缺失的參數(shù)并設(shè)置默認值,我們使用 OR 運算符(||)。這個運算符首先檢查第一個值,如果是 truthy,運算符會返回它,否則返回第二個參數(shù)。
這種方法在函數(shù)內(nèi)很常用,但也存在瑕疵。如果傳遞 0 或者 null 也會返回默認值。因為它們被認為是 falsy 值。所以如果我們確實需要給函數(shù)傳遞 0 或者 null,我們需要換種方法來檢測參數(shù)是否缺失:
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
在這個函數(shù)中,通過檢查參數(shù)的類型是否為 undefined 來確定是否要賦予默認值。這種方法代碼量稍微大一些,但更安全,可以讓我們給函數(shù)傳遞 0 或者 null。
ES6 中的默認參數(shù)ES6 中,我們不必再檢查參數(shù)是否為 undefined 來模擬默認參數(shù),我們可以直接將默認參數(shù)函數(shù)聲明中。
function foo(a = 10, b = 10) { console.log(a, b); } foo(5); // 5 10 foo(0, null); // 0 null
正如你所看到的,忽略參數(shù)返回了默認值,但傳遞 0 或者 null 并沒有。我們甚至可以使用函數(shù)來產(chǎn)生參數(shù)的默認值:
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)
需要注意的是,只有缺少第二個參數(shù)的時候,gegParam 方法才會執(zhí)行,所以當我們使用兩個參數(shù) multiply() 的時候并不會彈出 alert。
默認參數(shù)另一個有意思的特性是在方法聲明是可以引用其他參數(shù)和變量作為默認參數(shù):
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
甚至可以在函數(shù)聲明的時候執(zhí)行操作符:
function myFunction(a, b = ++a, c = a*b) { console.log(c); } myFunction(5); // 36
注意:不像其他語言,JavaScript 是在調(diào)用時才計算默認參數(shù)的:
function add(value, array = []) { array.push(value); return array; } add(5); // [5] add(6); // [6], not [5, 6]解構(gòu)賦值
解構(gòu)賦值是 ES6 的新特性,讓我們可以從數(shù)組或者對象中提取值并賦值給變量,語法上類似于對象和數(shù)組字面量。當給函數(shù)傳參時,這種語法清晰且易于理解并且很實用。
在 ES5 中,經(jīng)常使用配置對象來處理大量的的可選參數(shù),尤其是屬性的順序無關(guān)緊要的時候,看下面的函數(shù):
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 開發(fā)者經(jīng)常使用,并且很好用。但我們必須進入函數(shù)體內(nèi)才知道到底需要多少參數(shù),使用解構(gòu)參數(shù)賦值,我們可以在函數(shù)聲明時很清晰的指定需要的參數(shù)。
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);
在這個函數(shù)中,我們使用了對象解構(gòu)模式,而不是一個配置型對象,讓我們的代碼更加清晰易讀。
我們也可以混用解構(gòu)參數(shù)和普通參數(shù):
function initiateTransfer(param1, {protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer("some value", options);
需要注意,如果函數(shù)調(diào)用時解構(gòu)參數(shù)缺失會拋出一個類型錯誤:
function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer(); // TypeError: Cannot match against "undefined" or "null"
當我們的參數(shù)是必須的,這種行為我們是想要的,但是如果我們期望參數(shù)可選呢?為阻止這種錯誤,我們需要給解構(gòu)參數(shù)賦一個默認值:
function initiateTransfer({protocol, port, delay, retries, timeout, log} = {}) { // code to initiate transfer } initiateTransfer(); // no error
在這個函數(shù)中,我們給解構(gòu)參數(shù)賦了一個空對象作為默認值。現(xiàn)在如果函數(shù)調(diào)用時沒有賦予參數(shù),不會拋出錯誤。
我們也可以給解構(gòu)參數(shù)每個屬性都賦默認值:
function initiateTransfer({ protocol = "http", port = 800, delay = 150, retries = 10, timeout = 500, log = true }) { // code to initiate transfer }
在這個例子中,每個屬性都被賦予默認值,就無需在函數(shù)體內(nèi)手動檢查 undefined 的參數(shù)再賦予默認值。
參數(shù)傳遞函數(shù)傳參有兩種方式:引用傳遞和值傳遞。如果是引用傳遞,修改參數(shù)會引起全局的變化,如果是值傳遞,只會引起函數(shù)內(nèi)的變化。
在一些語言中,像 Visual Basic 和 PowerShell,我們可以選擇聲明是值傳遞還是引用傳遞,但 JavaScript 不是這樣。
值傳遞嚴格來說,JavaScript只能值傳遞。當我們通過值傳遞給函數(shù)傳參,就在函數(shù)作用域內(nèi)創(chuàng)建了這個值得副本。所以任何值得變化都只會反映在函數(shù)內(nèi)部。看下面的例子:
var a = 5; function increment(a) { a = ++a; console.log(a); } increment(a); // 6 console.log(a); // 5
在這里,在函數(shù)內(nèi)部修改修改參數(shù)并不會影響到原始值。所以在函數(shù)外打印這個變量,得到的結(jié)果始終是 5。
引用傳遞在 JavaScript 中,所有的都是值傳遞,但是當我們傳遞一個變量指向一個對象(包括數(shù)組),這個“值”就指向了這個對象,改變了對象的某個屬相也會引起其關(guān)聯(lián)對象的改變。
看這個函數(shù):
function foo(param){ param.bar = "new value"; } obj = { bar : "value" } console.log(obj.bar); // value foo(obj); console.log(obj.bar); // new value
正如你看到的,對象的屬性在函數(shù)體內(nèi)部被修改,但是卻影響到了函數(shù)外部的對象。
當我們傳遞一個非原始的值,像數(shù)組或者對象,程序會在內(nèi)存中創(chuàng)建一個對象,指向原始地址。如果被修改,原始值也會隨之修改。
類型檢查和缺失或多余參數(shù)在強類型的語言中,我們必須在函數(shù)聲明時聲明參數(shù)的類型,但 JavaScript 中沒有這種特性,在 JavaScript 中,并不關(guān)心傳遞給函數(shù)的參數(shù)的類型和個數(shù)。
假設(shè)我們有一個函數(shù),僅接受一個參數(shù)。當我們調(diào)用這個函數(shù)的使用,我們并不限制到底傳遞給函數(shù)多少個參數(shù),甚至可以選擇不傳,都不會產(chǎn)生錯誤。
參數(shù)的個數(shù)可以分為兩種情況:
參數(shù)缺失
缺失的變量賦值為 undefined
參數(shù)過多
多余的參數(shù)會被忽略,但可以從 arguments 變量中取到(下文即將討論)。
強制參數(shù)函數(shù)調(diào)用中如果函數(shù)缺失,它會被設(shè)置為 undefined。我們可以利用這一點,如果參數(shù)缺失就拋出錯誤:
function foo(mandatory, optional) { if (mandatory === undefined) { throw new Error("Missing parameter: mandatory"); } }
在 ES6 中,我們可以更近一步,使用默認參數(shù)來設(shè)置強制參數(shù):
function throwError() { throw new Error("Missing parameter"); } function foo(param1 = throwError(), param2 = throwError()) { // do something } foo(10, 20); // ok foo(10); // Error: missing parameterarguments 對象
在 ES4 的時候默認參數(shù)就被加入,來代替 arguments 對象,但 ES4 并沒有實現(xiàn)。隨著 ES6 的發(fā)布,JavaScript 現(xiàn)在官方支持了默認參數(shù)。但并沒有取消支持 arguments 的計劃。
arguments 對象是一個類數(shù)組的對象,可以在所有的函數(shù)中取到。arguments 通過數(shù)字索引來獲取傳入的參數(shù),而不是通過參數(shù)的名字。這個對象允許我們給函數(shù)傳入任意多的參數(shù)。看下面的代碼判斷:
function checkParams(param1) { console.log(param1); // 2 console.log(arguments[0], arguments[1]); // 2 3 console.log(param1 + arguments[0]); // 4 } checkParams(2, 3);
這個函數(shù)期望傳入一個參數(shù),當我們傳入兩個參數(shù)調(diào)用它的時候,我們通過 param1 或者 arguments[0] 來獲取第一個參數(shù),但第二個參數(shù)只能通過 arguments[1] 獲取。也即是說,arguments 對象可以和有命名的參數(shù)一起使用。
arguments 對象包含了所有傳入函數(shù)的參數(shù),并且索引的起始是 1。當我們希望獲取更多的參數(shù)的時候,我們會使用 arguments[2] 、arguments[3] 等等。
我們可以跳過所有的參數(shù)命名設(shè)置,僅僅使用 arguments 對象:
function checkParams() { console.log(arguments[1], arguments[0], arguments[2]); } checkParams(2, 4, 6); // 4 2 6
實際上,命名的參數(shù)是一種方便,但不是必需的。同樣的,rest 參數(shù)也可以用來顯示傳入的參數(shù):
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 對象是一個類數(shù)組對象,但是缺少像 slice 和 foreach 等方法。為了在 arguments 對象上使用這些方法,需要將其轉(zhuǎn)換為真實的數(shù)組:
function sort() { var a = Array.prototype.slice.call(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]
在這個函數(shù)中,使用 Array.prototype.slice.call() 快速將 arguments 對象轉(zhuǎn)換為數(shù)組。然后使用 sort 方法進行排序。
ES6 有一種更直接的方法,Array.from(),ES6 新增的方法,用來通過類數(shù)組對象創(chuàng)建一個新的數(shù)組。
function sort() { var a = Array.from(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]length 屬性
雖然 arguments 對象并不是嚴格意義的數(shù)組,但它有一個 length 屬性,可以用來檢查傳遞給函數(shù)的參數(shù)的個數(shù)。
function countArguments() { console.log(arguments.length); } countArguments(); // 0 countArguments(10, null, "string"); // 3
通過使用 length 屬性,我們可以更好的控制參數(shù)的數(shù)量。比如說,如果一個函數(shù)需要兩個參數(shù),我們就可以使用 length 屬性來檢查參數(shù)數(shù)量,如果少于期望數(shù)量就拋出錯誤。
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 } }
rest 參數(shù)是數(shù)組,所以他也有 length 屬性,我們用 ES6 來重寫上面的方法:
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 屬性指向當前正在運行的函數(shù),而 caller 指向調(diào)用當前正在運行函數(shù)的函數(shù)。在 ES5 嚴格模式下,這些屬性是被廢棄掉的,如果要訪問它們會拋出錯誤。
arguments.callee 屬性在遞歸函數(shù)(遞歸函數(shù)是一個普通函數(shù),通過它的簽名指向自身)下很有用,尤其是函數(shù)的簽名不可用時(也就是匿名函數(shù))。因為匿名函數(shù)沒有名字,唯一指向自身的方法就是通過 arguments.callee。
var result = (function(n) { if (n <= 1) { return 1; } else { return n * arguments.callee(n - 1); } })(4); // 24嚴格模式和非嚴格模式下的 arguments
在 ES5 非嚴格模式下, arguments 對象有一個不常用的特性:它保持和命名參數(shù)值同步。
function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // true return param } foo(200); // 500
在函數(shù)內(nèi)部,一個新的值賦給 arguments[0]。因為 arguments 一直和命名參數(shù)的值保持同步,arguments[0] 的改變也會引起 param 的改變。事實上,他們是同個變量的不同名稱。在 ES5 嚴格模式下,這種令人迷惑的特性被移除了:
"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,并且輸出和期望一樣。ES6下,輸出結(jié)果和 ES5 的嚴格模式是一致的。但是請記住,在函數(shù)聲明時使用了默認參數(shù),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");
在這個函數(shù)中,盡管 param3 有默認值,但他和 arguments[2] 并不相等,因為只有兩個參數(shù)傳入了函數(shù)。也就是說,設(shè)置默認參數(shù)并不影響 arguments 對象。
結(jié)論ES6 給 JavaScript 帶來了許多大大小小的改進。越來越多的開發(fā)者開始使用 ES6,而且很多所有的特性都可以無障礙使用。本文我們學習了 ES6 是如何提升JavaScript 處理參數(shù)的能力的。但我們僅僅學了 ES6 的一點皮毛。更多的有趣的特性等著我們?nèi)ネ诰颍?/p>
ECMAScript 6 Compatibility Table, Juriy Zaytsev
“ECMAScript 2015 Language Specification,” ECMA International
看下時間現(xiàn)在正好是23:23,幾乎用了一個下午和晚上把這篇文章讀完又翻譯完,這篇文章結(jié)合 ES5 和 ES6 來講解,收益頗多。不過翻譯水平有限,求多提意見多多指教 ~
原文地址: How To Use Arguments And Parameters In ECMAScript 6
小廣告
歡迎關(guān)注我們的微信公眾號:
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/81955.html
摘要:函數(shù)默認值是一個很提高魯棒性的東西就是讓程序更健壯關(guān)于函數(shù)默認參數(shù)的描述函數(shù)默認參數(shù)允許在沒有值或被傳入時使用默認形參。也就實現(xiàn)了上邊三元運算符的功能。直接使用這種方式,省去了在函數(shù)內(nèi)部進行默認值的檢查,能夠讓函數(shù)專注的做它應該做的事情。 函數(shù)默認值是一個很提高魯棒性的東西(就是讓程序更健壯)MDN關(guān)于函數(shù)默認參數(shù)的描述:函數(shù)默認參數(shù)允許在沒有值或undefined被傳入時使用默認形參...
摘要:更新了個版本,最新正式版是語言的下一代標準,早已在年月正式發(fā)布。基本不支持移動端瀏覽器對的支持情況版起便可以支持的新特性。比較通用的工具方案有,,,等。 1、ECMAScript是什么? 和 JavaScript 有著怎樣的關(guān)系? 1996 年 11 月,Netscape 創(chuàng)造了javascript并將其提交給了標準化組織 ECMA,次年,ECMA 發(fā)布 262 號標準文件(ECMA-...
摘要:函數(shù)的擴展函數(shù)參數(shù)的默認值之前,不能直接為函數(shù)的參數(shù)指定默認值,只能采用變通的方法。箭頭函數(shù)引入了一種新的函數(shù),叫做箭頭函數(shù)。箭頭函數(shù)和普通函數(shù)的行為非常相似,但是在語法構(gòu)成上非常不同。意味著函數(shù)內(nèi)的的值是全局對象,不是對象。 函數(shù)的擴展 函數(shù)參數(shù)的默認值 ES6 之前,不能直接為函數(shù)的參數(shù)指定默認值,只能采用變通的方法。 function log(x, y) { y = y ||...
閱讀 892·2021-10-13 09:39
閱讀 1481·2021-10-11 10:57
閱讀 2589·2019-08-26 13:53
閱讀 2538·2019-08-26 12:23
閱讀 3680·2019-08-23 18:30
閱讀 3744·2019-08-23 18:08
閱讀 2523·2019-08-23 18:04
閱讀 2958·2019-08-23 16:28