摘要:形參默認(rèn)值不再影響對(duì)象當(dāng)使用默認(rèn)參數(shù)值時(shí),對(duì)象的行為與以往有所不同。具有方法的函數(shù)被統(tǒng)稱為構(gòu)造函數(shù)。當(dāng)調(diào)用函數(shù)的方法時(shí),被賦值為新創(chuàng)建對(duì)象實(shí)例如果調(diào)用方法,則的值為。
由于JavaScript開發(fā)者多年的不斷抱怨和呼吁,ES6終于大力度地更新了函數(shù)特性,在ES5基礎(chǔ)上進(jìn)行了許多改進(jìn)。
函數(shù)形參的默認(rèn)值 ES5形參默認(rèn)值的實(shí)現(xiàn)在ES5中,你很可能通過(guò)以下這種方式為函數(shù)賦予默認(rèn)值:
function makeRequest(url, timeout, callback) { timeout = timeout || 2000; callback = callback || function() {}; // 函數(shù)的其余部分 }
對(duì)于函數(shù)的命名參數(shù),如果不顯式傳值,則其默認(rèn)值為undefined。因此經(jīng)常是使用邏輯或操作符來(lái)為缺失的參數(shù)提供默認(rèn)值。然而這個(gè)方式有缺陷,如果給第二個(gè)形參timeout傳入值0,盡管這個(gè)值是合法的,也會(huì)被視為false值,對(duì)函數(shù)調(diào)用方來(lái)說(shuō),timeout非預(yù)期的被修改為2000。
更安全的選擇是通過(guò)typeof檢查參數(shù)類型,就像這樣:
function makeRequest(url, timeout, callback) { timeout = (typeof timeout !== "undefined") ? timeout : 2000; callback = (typeof callback !== "undefined") ? callback : function() {}; // 函數(shù)的其余部分 }
在流行的JavaScript庫(kù)中均使用類似的模式進(jìn)行默認(rèn)值補(bǔ)全。
ES6形參默認(rèn)值的實(shí)現(xiàn)ES6簡(jiǎn)化了為形參提供默認(rèn)值的過(guò)程,定義形參時(shí)即可指定初始值:
function makeRequest(url, timeout = 2000, callback) { // 函數(shù)的其余部分 }
這種情況下,只有當(dāng)不為第二個(gè)參數(shù)傳值或主動(dòng)為第二個(gè)參數(shù)傳入undefined時(shí)才會(huì)使用timeout的默認(rèn)值,就像這樣:
// 使用timeout的默認(rèn)值 makeRequest("/foo", undefined, function(body) { doSomething(body); }); // 使用timeout的默認(rèn)值 makeRequest("/foo"); // 不使用timeout的默認(rèn)值 makeRequest("foo", null, function(body){ // null是一個(gè)合法值 doSomething(body); });ES6默認(rèn)參數(shù)表達(dá)式
ES6中,關(guān)于默認(rèn)參數(shù)值,還可以是非原始值傳參,就像這樣:
function getValue() { return 5; } function add(first, second = getValue()) { return first + second; } console.log(add(1, 1)); // 2 console.log(add(1)); // 6
尤其注意,初次解析函數(shù)聲明時(shí)是不會(huì)調(diào)用getValue()方法的,只有當(dāng)調(diào)用add()函數(shù)且不傳入第二個(gè)參數(shù)時(shí)才會(huì)被調(diào)用。另外,second = getValue()這里的小括號(hào)()不要忘記掉,不然最終傳入的是對(duì)函數(shù)的引用,而不是函數(shù)調(diào)用的結(jié)果。
正因?yàn)槟J(rèn)參數(shù)是在函數(shù)調(diào)用時(shí)求值,所以可以使用先定義的參數(shù)作為后定義的參數(shù)的默認(rèn)值,就像這樣:
function add(first, second = first) { return first + second; } console.log(add(1, 1)); // 2 console.log(add(1)); // 2
也可以將參數(shù)first傳入一個(gè)函數(shù)來(lái)獲得參數(shù)second的值,就像這樣:
function getValue(value) { return value + 5; } function add(first, second = getValue(first)) { return first + second; } console.log(add(1, 1)); // 2 console.log(add(1)); // 7默認(rèn)參數(shù)的臨時(shí)死區(qū)
在引用參數(shù)默認(rèn)值的時(shí),只允許引用前面參數(shù)的值,即先定義的參數(shù)不能訪問(wèn)后定義的參數(shù):
function add(first = second, second) { return first + second; } console.log(add(1, 1)); // 2 console.log(add(undefined, 1)); // 拋出錯(cuò)誤
在講解let和const時(shí)介紹了臨時(shí)死區(qū)(TDZ),其實(shí)默認(rèn)參數(shù)也有同樣的臨時(shí)死區(qū)。上面這個(gè)示例,調(diào)用add(1, 1)和add(undefined, 1)相當(dāng)于引擎在背后做了如下事情:
// 表示調(diào)用add(1, 1)時(shí)的JavaScript代碼 let first = 1; let second = 1; // 表示調(diào)用add(undefined, 1)時(shí)的JavaScript代碼 let first = second; let second = 1;
這個(gè)示例中,調(diào)用add(undefined, 1)函數(shù),因?yàn)楫?dāng)first初始化時(shí)second尚未初始化,所以會(huì)導(dǎo)致程序拋出錯(cuò)誤,此時(shí)second(已聲明,未初始化)尚處于臨時(shí)死區(qū)中,正如討論let綁定時(shí)所說(shuō)的那樣,任何引用臨時(shí)死區(qū)中的綁定的行為都會(huì)報(bào)錯(cuò)。
函數(shù)參數(shù)有自己的作用域和臨時(shí)死區(qū),其與函數(shù)體的作用域是各自獨(dú)立的,也就是說(shuō)參數(shù)的默認(rèn)值不可訪問(wèn)函數(shù)體內(nèi)聲明的變量。
ES6形參默認(rèn)值不再影響arguments對(duì)象當(dāng)使用默認(rèn)參數(shù)值時(shí),arguments對(duì)象的行為與以往有所不同。在ES5非嚴(yán)格模式下,函數(shù)命名參數(shù)的變化會(huì)體現(xiàn)在arguments對(duì)象中:
function mixArgs(first, second) { console.log(first === arguments[0]); console.log(second === arguments[1]); first = "c"; second = "d"; console.log(first === arguments[0]); consolo.log(second === arguments[1]); } mixArgs("a", "b");
這段代碼會(huì)輸出:
true true true true
在ES5非嚴(yán)格模式下,命名參數(shù)的變化會(huì)同步更新到arguments對(duì)象中,所以當(dāng)first和second被賦予新值時(shí),arguments[0]和arguments[1]相應(yīng)地也就更新了,最終所有===全等結(jié)果都為true。
在ES5嚴(yán)格模式下,取消了arguments對(duì)象的這個(gè)令人感到困惑的行為,無(wú)論參數(shù)如何變化,arguments對(duì)象不再隨之改變。ES6中,arguments對(duì)象的行為與ES5嚴(yán)格模式下保持一致。arguments對(duì)象保持與命名參數(shù)分離,這個(gè)微妙的細(xì)節(jié)將影響你使用arguments對(duì)象的方式,請(qǐng)看以下代碼:
function mixArgs(first, second = "b") { console.log(arguments.length); console.log(first === arguments[0]); console.log(second === arguments[1]); first = "c"; second = "d"; console.log(first === arguments[0]); console.log(second === arguments[1]); } mixArgs("a");
這段代碼會(huì)輸出以下內(nèi)容:
1 true false false false false
改變first和second并不會(huì)影響arguments對(duì)象。總是可以通過(guò)arguments對(duì)象將參數(shù)恢復(fù)為初始值。
處理無(wú)命名參數(shù)JavaScript的函數(shù)語(yǔ)法規(guī)定,無(wú)論函數(shù)已定義的命名參數(shù)有多少,都不限制調(diào)用時(shí)傳入的實(shí)際參數(shù)數(shù)量。當(dāng)傳入更少數(shù)量的參數(shù)時(shí),默認(rèn)參數(shù)值的特性可以有效簡(jiǎn)化函數(shù)的聲明;當(dāng)傳入更多數(shù)量的參數(shù)時(shí),ES6同樣也提供了更好的方案。
ES5中的無(wú)命名參數(shù)早先,我們用JavaScript提供的arguments對(duì)象來(lái)檢查函數(shù)的所有參數(shù),從而不必定義每一個(gè)要用的參數(shù),看下面的例子:
function pick(object) { let result = Object.create(null); // 從第二個(gè)參數(shù)開始 for(let i = 1, len = arguments.length; i < len; i++) { result[arguments[i]] = object[arguments[i]]; } return result; } let book = { title: "ECMAScript 6", author: "Nicholas", year: 2016 }; let bookData = pick(book, "author", "year"); console.log(bookData.author); // "Nicholas" console.log(bookData.year); // 2016
這個(gè)函數(shù)模仿了Underscore.js庫(kù)中的pick()方法,返回一個(gè)給定對(duì)象的副本,包含原始對(duì)象屬性的特定子集。在這個(gè)示例中只定義了一個(gè)參數(shù),第一個(gè)參數(shù)傳入的是被復(fù)制的源對(duì)象,其他參數(shù)為被復(fù)制屬性的名稱。
關(guān)于pick()函數(shù)應(yīng)該注意這樣幾件事:首先,并不容易發(fā)現(xiàn)這個(gè)函數(shù)可以接受任意數(shù)量的參數(shù),當(dāng)然,可以定義更多的參數(shù),但是怎么也達(dá)不到要求;其次,因?yàn)榈谝粋€(gè)參數(shù)為命名參數(shù)并已被使用,當(dāng)你要查找需要拷貝的屬性名稱時(shí),不得不從索引1而不是索引0開始遍歷arguments對(duì)象。牢記真正的索引位置并不難,但這總歸是我們需要牽掛的問(wèn)題。
ES6中,通過(guò)引入不定參數(shù)(rest parameters)的特性可以輕易解決這些問(wèn)題。
ES6的不定參數(shù)在函數(shù)的命名參數(shù)前添加三個(gè)點(diǎn)(...)就表明這是一個(gè)不定參數(shù),該參數(shù)為一個(gè)數(shù)組,包含著自它之后傳入的所有參數(shù)。使用不定參數(shù)重寫pick()函數(shù):
function pick(object, ...keys) { let result = Object.create(null); for(let i = 0, len = keys.length; i < len; i++) { result[keys[i]] = object[keys[i]]; } return result; }
只需要看一眼就知曉該函數(shù)可以處理的參數(shù)數(shù)量。
注意一下,函數(shù)的length屬性統(tǒng)計(jì)的是函數(shù)命名參數(shù)的數(shù)量,不定參數(shù)的加入不會(huì)影響函數(shù)length屬性。在本示例中pick()函數(shù)的length值為1,因?yàn)橹粫?huì)計(jì)算object。另外每個(gè)函數(shù)最多只能聲明一個(gè)不定參數(shù),并且一定要放在末尾。
不定參數(shù)的設(shè)計(jì)初衷是代替JavaScript的arguments對(duì)象的。但是arguments對(duì)象依然存在。如果聲明函數(shù)時(shí)定義了不定參數(shù),則在函數(shù)被調(diào)用時(shí),arguments對(duì)象包含了所有傳入函數(shù)的參數(shù),就像這樣:
function checkArgs(...args) { console.log(args.length); console.log(arguments.length); console.log(args[0], arguments[0]); console.log(args[1], arguments[1]); } checkArgs("a", "b");
調(diào)用checkArgs(),輸出以下內(nèi)容:
2 2 a a b b
無(wú)論是否使用不定參數(shù),arguments對(duì)象總是包含所有傳入函數(shù)的參數(shù)。
展開運(yùn)算符配合不定參數(shù)展開運(yùn)算符可以讓你指定一個(gè)數(shù)組,將它們打散后作為各自獨(dú)立的參數(shù)傳入函數(shù)。正好,不定參數(shù)希望的就是指定多個(gè)各自獨(dú)立的參數(shù),并通過(guò)整合后的數(shù)組來(lái)訪問(wèn):
function max(...args) { // 計(jì)算并返回最大值 } let values = [10, 20, 30, 40]; // 使用展開運(yùn)算符打散 max(...values); // 等價(jià)于 // max(10, 20, 30, 40)構(gòu)造函數(shù)
ES5及早期版本中的函數(shù)具有多重功能,可以結(jié)合new使用,函數(shù)內(nèi)的this值將指向一個(gè)新對(duì)象,函數(shù)最終會(huì)返回這個(gè)新對(duì)象:
function Person(name) { this.name = name; } var person = new Person("Nicholas"); var notAPerson = Person("Nicholas"); console.log(person); // "[Object Object]" console.log(notAPerson); // "undefined"
JavaScript函數(shù)有兩個(gè)不同的內(nèi)部方法:[[Call]]和[[Construct]]。當(dāng)通過(guò)new關(guān)鍵字調(diào)用函數(shù)時(shí),執(zhí)行的是[[Construct]]函數(shù),它負(fù)責(zé)創(chuàng)建一個(gè)通常被稱作實(shí)例的新對(duì)象,然后再執(zhí)行函數(shù)體,將this綁定到實(shí)例上,并返回這個(gè)對(duì)象;如果不通過(guò)new關(guān)鍵字調(diào)用函數(shù),則執(zhí)行[[Call]]函數(shù),從而直接執(zhí)行代碼中的函數(shù)體。具有[[Construct]]方法的函數(shù)被統(tǒng)稱為構(gòu)造函數(shù)。
不是所有函數(shù)都有[[Construct]]方法,因此不是所有函數(shù)都可以通過(guò)new來(lái)調(diào)用,比如ES6的箭頭函數(shù)就沒(méi)有這個(gè)[[Construct]]。
ES5判斷函數(shù)是否用new調(diào)用ES5中,如果想判斷一個(gè)函數(shù)是否通過(guò)new關(guān)鍵字被調(diào)用,最流行的方式是使用instanceof:
function Person(name) { if(this instanceof Person) { this.name = name; // 如果通過(guò)new關(guān)鍵字調(diào)用 } else { throw new Error("必須通過(guò)new關(guān)鍵字來(lái)調(diào)用Person。"); } } var person = new Person("Nicholas"); var notAPerson = Person("Nicholas"); // 拋出錯(cuò)誤
但是這個(gè)方法也不完全可靠,因?yàn)橛幸环N不通過(guò)new關(guān)鍵字的方法也可以將this綁定到Person的實(shí)例上:
function Person(name) { if(this instanceof Person) { this.name = name; } else { throw new Error("必須通過(guò)new關(guān)鍵字來(lái)調(diào)用Person。"); } } var person = new Person("Nicholas"); var notAPerson = Person.call(person, "Michael"); // 有效!
調(diào)用Person.call()時(shí)將變量person傳入作為第一個(gè)參數(shù),相當(dāng)于在Person函數(shù)里將this設(shè)為了person實(shí)例。對(duì)于函數(shù)本身,無(wú)法區(qū)分是通過(guò)Person.call()(或者是Person.apply())還是new關(guān)鍵字調(diào)用得到的Person的實(shí)例。
ES6判斷函數(shù)是否用new調(diào)用為了解決判斷函數(shù)是否通過(guò)new關(guān)鍵字調(diào)用的問(wèn)題,ES6引入了new.target這個(gè)元屬性。當(dāng)調(diào)用函數(shù)的[[Construct]]方法時(shí),new.target被賦值為新創(chuàng)建對(duì)象實(shí)例;如果調(diào)用[[Call]]方法,則new.target的值為undefined。
可以通過(guò)檢查new.target是否被定義過(guò)來(lái)安全的檢測(cè)一個(gè)函數(shù)是否是通過(guò)new關(guān)鍵字調(diào)用的,就像這樣:
function Person(name) { // typeof new.target === Person 可以檢查是否被某個(gè)特定的構(gòu)造函所調(diào)用 if(typeof new.target !== "undefined") { this.name = name; } else { throw new Error("必須通過(guò)new關(guān)鍵字來(lái)調(diào)用Person。"); } } var person = new Person("Nicholas"); var notAPerson = Person.call(person, "Michael"); // 拋出錯(cuò)誤!箭頭函數(shù)
在ES6中,箭頭函數(shù)是最有趣的新增特性。它與傳統(tǒng)的JavaScript函數(shù)有些許不同,主要集中在以下方面:
沒(méi)有this、super、arguments和new.target綁定
箭頭函數(shù)中的this、super、arguments及new.target這些值由外圍最近一層非箭頭函數(shù)決定。
不能通過(guò)new關(guān)鍵字調(diào)用
箭頭函數(shù)沒(méi)有[[Construct]]方法,所以不能被用作構(gòu)造函數(shù),如果通過(guò)new關(guān)鍵字調(diào)用箭頭函數(shù),程序會(huì)拋出錯(cuò)誤。
沒(méi)有原型
由于不可以通過(guò)new關(guān)鍵字調(diào)用箭頭函數(shù),因而沒(méi)有構(gòu)建原型的需求,所以箭頭函數(shù)不存在prototype這個(gè)屬性。
不可以改變this的綁定
函數(shù)內(nèi)部的this值不可被改變,在函數(shù)的生命周期內(nèi)始終保持一致。
不支持arguments對(duì)象
箭頭函數(shù)沒(méi)有arguments綁定,所以你必須通過(guò)命名參數(shù)和不定參數(shù)這兩種形式訪問(wèn)函數(shù)的參數(shù)。
不支持重復(fù)的命名參數(shù)
箭頭函數(shù)不支持重復(fù)的命名參數(shù);而在傳統(tǒng)函數(shù)的規(guī)定中,只有在嚴(yán)格模式下才不能有重復(fù)的命名參數(shù)。
this綁定是JavaScript程序中常見的錯(cuò)誤來(lái)源,在函數(shù)內(nèi)很容易就對(duì)this的值失去控制,其經(jīng)常導(dǎo)致程序出現(xiàn)意想不到的行為,箭頭函數(shù)消除了這方面的煩惱。
箭頭函數(shù)的語(yǔ)法單一參數(shù):
let reflect = value => value; // 相當(dāng)于 let reflect = function(value) { return value; };
兩個(gè)以上參數(shù):
let sum = (num1, num2) => num1 + num2; // 相當(dāng)于 let sum = function(num1, num2) { return num1 + num2; };
沒(méi)有參數(shù):
let getName = () => "Nicholas"; // 相當(dāng)于 let getName = function(){ return "Nicholas"; };
多表達(dá)式組成的更傳統(tǒng)的函數(shù)體:
let sum = (num1, num2) => { return num1 + num2; }; // 相當(dāng)于 let sum = function(num1, num2) { return num1 + num2; };
空函數(shù):
let doNothing = () => {}; // 相當(dāng)于 let doNothing = function(){};
返回對(duì)象字面量:
let getTempItem = id => ({ id: id, name: "Temp" }); // 相當(dāng)于 let getTempItem = function(id){ return { id: id, name: "Temp" }; };
將對(duì)象字面量包裹在小括號(hào)中是為了將其與函數(shù)體區(qū)分開來(lái)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/84960.html
摘要:前言在了解是如何編譯前,我們先看看的和的構(gòu)造函數(shù)是如何對(duì)應(yīng)的。這是它跟普通構(gòu)造函數(shù)的一個(gè)主要區(qū)別,后者不用也可以執(zhí)行。該函數(shù)的作用就是將函數(shù)數(shù)組中的方法添加到構(gòu)造函數(shù)或者構(gòu)造函數(shù)的原型中,最后返回這個(gè)構(gòu)造函數(shù)。 前言 在了解 Babel 是如何編譯 class 前,我們先看看 ES6 的 class 和 ES5 的構(gòu)造函數(shù)是如何對(duì)應(yīng)的。畢竟,ES6 的 class 可以看作一個(gè)語(yǔ)法糖,...
摘要:采用的生成非波拉契數(shù)列提供了原生的支持,語(yǔ)法非常有特色,關(guān)鍵字后面緊跟一個(gè)星號(hào)。的詳細(xì)介紹參考官網(wǎng)先看如何用這個(gè)黑科技重新實(shí)現(xiàn)非波拉契樹立的生成。在這個(gè)內(nèi)部,我們定義了一個(gè)無(wú)限循環(huán),用于計(jì)算非波拉契數(shù)列。 程序員面試系列 Java面試系列-webapp文件夾和WebContent文件夾的區(qū)別? 程序員面試系列:Spring MVC能響應(yīng)HTTP請(qǐng)求的原因? Java程序員面試系列-什么...
摘要:回顧我們先來(lái)回顧下箭頭函數(shù)的基本語(yǔ)法。主要區(qū)別包括沒(méi)有箭頭函數(shù)沒(méi)有,所以需要通過(guò)查找作用域鏈來(lái)確定的值。箭頭函數(shù)并沒(méi)有方法,不能被用作構(gòu)造函數(shù),如果通過(guò)的方式調(diào)用,會(huì)報(bào)錯(cuò)。 回顧 我們先來(lái)回顧下箭頭函數(shù)的基本語(yǔ)法。 ES6 增加了箭頭函數(shù): let func = value => value; 相當(dāng)于: let func = function (value) { return ...
摘要:今天介紹怎么編譯的各種函數(shù)和語(yǔ)法。對(duì)于相關(guān)的匹配規(guī)則,除了匹配結(jié)尾的文件,還應(yīng)該去除文件夾下的第三庫(kù)的文件發(fā)布前已經(jīng)被處理好了。它需要在我們項(xiàng)目的入口文件中被引入,或者在中配置。個(gè)人網(wǎng)站原文鏈接系列教程二編譯 今天介紹webpack怎么編譯ES6的各種函數(shù)和語(yǔ)法。敲黑板:這是webpack4版本哦, 有一些不同于webpack3的地方。 >>> 本節(jié)課源碼 >>> 所有課程源碼 1....
摘要:以上的代碼對(duì)應(yīng)到就是調(diào)用父類的值得注意的是關(guān)鍵字表示父類的構(gòu)造函數(shù),相當(dāng)于的。舉個(gè)例子這是因?yàn)樽鳛闃?gòu)造函數(shù)的語(yǔ)法糖,同時(shí)有屬性和屬性,因此同時(shí)存在兩條繼承鏈。子類的屬性,表示構(gòu)造函數(shù)的繼承,總是指向父類。 前言 在上一篇 《 ES6 系列 Babel 是如何編譯 Class 的(上)》,我們知道了 Babel 是如何編譯 Class 的,這篇我們學(xué)習(xí) Babel 是如何用 ES5 實(shí)現(xiàn)...
摘要:正是因?yàn)樗鼪](méi)有,所以也就不能用作構(gòu)造函數(shù)。不可以當(dāng)作構(gòu)造函數(shù),也就是說(shuō),不可以使用命令,否則會(huì)拋出一個(gè)錯(cuò)誤。不可以使用對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。 箭頭函數(shù) 在之前ES5的版本中,我們定義一個(gè)函數(shù)的形式如下: function a() { // do something…… } 但是在ES6中,則新增了箭頭函數(shù)的方式,ES6中允許使用箭頭(=>)來(lái)定義函數(shù)。 () => { ...
閱讀 3402·2021-11-22 15:22
閱讀 2382·2021-09-06 15:00
閱讀 885·2020-06-22 14:39
閱讀 3712·2019-08-30 15:56
閱讀 1549·2019-08-30 12:55
閱讀 3284·2019-08-29 17:19
閱讀 3238·2019-08-26 11:41
閱讀 623·2019-08-23 17:14