摘要:主要知識點有函數(shù)參數(shù)默認(rèn)值剩余參數(shù)擴(kuò)展運(yùn)算符屬性塊級函數(shù)箭頭函數(shù)以及尾調(diào)用優(yōu)化深入理解筆記目錄函數(shù)的默認(rèn)參數(shù)在中,我們給函數(shù)傳參數(shù),然后在函數(shù)體內(nèi)設(shè)置默認(rèn)值,如下面這種方式。擁有方法的函數(shù)被稱為構(gòu)造器。
主要知識點有:函數(shù)參數(shù)默認(rèn)值、剩余參數(shù)、擴(kuò)展運(yùn)算符、new.target屬性、塊級函數(shù)、箭頭函數(shù)以及尾調(diào)用優(yōu)化
《深入理解ES6》筆記 目錄
函數(shù)的默認(rèn)參數(shù)在ES5中,我們給函數(shù)傳參數(shù),然后在函數(shù)體內(nèi)設(shè)置默認(rèn)值,如下面這種方式。
function a(num, callback) { num = num || 6 callback = callback || function (data) {console.log("ES5: ", data)} callback(num * num) } a() //ES5: 36,不傳參輸出默認(rèn)值 //你還可以這樣使用callback a(10, function(data) { console.log(data * 10) // 1000, 傳參輸出新數(shù)值 })
弊端:此處的 num 的有效值實際上有可能是 0 ,但因為 0 是假值,就會導(dǎo)致 num 的值在這種情況下會被替換為 6;
可以用 typeof 來檢測參數(shù)的類型:
function a(num, callback) { num = (typeof num!== "undefined") ? num: 6; callback = (typeof callback !== "undefined") ? callback : function (data) {console.log("ES5: ", data)}; callback(num * num) }
雖然這種方法更安全,但依然為實現(xiàn)一個基本需求而書寫了過多的代碼。它代表了一種公共
模式,而流行的 JS 庫中都充斥著類似的模式。
function a(num = 6, callback = function (data) {console.log("ES6: ", data)}) { callback(num * num) } a() //ES6: 36, 不傳參輸出默認(rèn)值 a(10, function(data) { console.log(data * 10) // 1000,傳參輸出新數(shù)值 })
使用ES6的默認(rèn)值寫法可以讓函數(shù)體內(nèi)部的代碼更加簡潔優(yōu)雅
參數(shù)默認(rèn)值如何影響 arguments 對象ES5 的非嚴(yán)格模式下
function mixArgs(first, second) { 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", "b"); //輸出 true true true true
ES5 的嚴(yán)格模式下
function mixArgs(first, second) { "use strict"; 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", "b"); //輸出 true true false false
ES6
arguments 對象的表現(xiàn)總是會與 ES5 的嚴(yán)格模式一致,無論此時函數(shù)是否明確運(yùn)行在嚴(yán)格模式下。
// 非嚴(yán)格模式 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"); //輸出 1 true false false false
此時arguments.length =1 ,因為只給 mixArgs() 傳遞了一個參數(shù)。這也意味著arguments[1] 的值是 undefined ,符合將單個參數(shù)傳遞給函數(shù)時的預(yù)期;這同時意味著first 與 arguments[0] 是相等的。改變 first 和 second 的值不會對 arguments 對象造成影響,無論是否在嚴(yán)格模式下,所以你可以始終依據(jù) arguments 對象來反映初始調(diào)用狀態(tài)。
默認(rèn)參數(shù)表達(dá)式參數(shù)不僅可以設(shè)置默認(rèn)值為字符串,數(shù)字,數(shù)組或者對象,還可以是一個函數(shù)。
function add() { return 10 } function a(num = add()){ console.log(num) } a() // 10默認(rèn)參數(shù)的臨時死區(qū)
第一章我們提到了let和const什么變量的臨時死區(qū)(TDZ),默認(rèn)參數(shù)既然是參數(shù),那么也同樣有臨時死區(qū),函數(shù)的作用域是獨(dú)立的,a函數(shù)不能共享b函數(shù)的作用域參數(shù)。
//這是個默認(rèn)參數(shù)臨時死區(qū)的例子,當(dāng)初始化a時,b還沒有聲明,所以第一個參數(shù)對b來說就是臨時死區(qū)。 function add(a = b, b){ console.log(a + b) } add(undefined, 2) // b is not define處理無命名參數(shù)
上面說的參數(shù)都是命名參數(shù),而無命名參數(shù)也是函數(shù)傳參時經(jīng)常用到的。當(dāng)傳入的參數(shù)是一個對象,不是一個具體的參數(shù)名,則是無命名參數(shù)。
function add(object){ console.log(object.a + object.b) } let obj = { a: 1, b: 2 } add(obj) // 3不定參數(shù)
使用...(展開運(yùn)算符)的參數(shù)就是不定參數(shù),它表示一個數(shù)組。
function add(...arr){ console.log(a + b) } let a = 1,b = 2 add(a, b) // 3
不定參數(shù)的使用限制:
函數(shù)只能有一個剩余參數(shù),并且它必須被放在最后
//錯誤的寫法1 function add(...arr, c){ console.log(a + b) } let a = 1,b = 2,c = 3 add(a, b, c)
剩余參數(shù)不能在對象字面量的 setter 屬性中使用
//錯誤的寫法2 let obj = { set add(...arr) { } }
剩余參數(shù)如何影響 arguments 對象
arguments 對象在函數(shù)被調(diào)用時反映了傳入的參數(shù),與剩余參數(shù)能協(xié)同工作,就像如下程序所演示的:
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"); //輸出 2 2 a a b b
arguments 對象總能正確反映被傳入函數(shù)的參數(shù),而無視剩余參數(shù)的使用。
ES6中的構(gòu)造函數(shù)Function新增了支持默認(rèn)參數(shù)和不定參數(shù)。
擴(kuò)展運(yùn)算符考慮一下Math.max()方法,它接受任意數(shù)量的參數(shù),并會返回其中的最大值。
//兩個值進(jìn)行比較 let value1 = 25, value2 = 50; console.log(Math.max(value1, value2)); // 50 //一個數(shù)組中找到最大值(es5) let values = [25, 50, 75, 100] console.log(Math.max.apply(Math, values)); //es6 let values = [25, 50, 75, 100] // 等價于 console.log(Math.max(25, 50, 75, 100)); console.log(Math.max(...values)); // 100
擴(kuò)展運(yùn)算符傳遞參數(shù)
//假設(shè)你想讓 Math.max() 返回的最小值為 0 (以防數(shù)組中混入了負(fù)值),你可以將參數(shù) 0 多帶帶傳入,并繼續(xù)為其他參數(shù)使用擴(kuò)展運(yùn)算符 let values = [-25, -50, -75, -100] console.log(Math.max(...values, 0)); // 0ES6 的名稱屬性
ES6 給所有函數(shù)添加了 name 屬性。
選擇合適的名稱//函數(shù)聲明 function doSomething() { // ... } //匿名函數(shù)表達(dá)式 var doAnotherThing = function() { // ... }; console.log(doSomething.name); // "doSomething" console.log(doAnotherThing.name); // "doAnotherThing"名稱屬性的特殊情況
//doSomethingElse的優(yōu)先級高于doSomething var doSomething = function doSomethingElse() { // ... }; //person.firstName 實際是個 getter 函數(shù),因此它的名稱是 "get firstName" var person = { get firstName() { return "Nicholas" }, sayName: function() { console.log(this.name); } } console.log(doSomething.name); // "doSomethingElse" console.log(person.sayName.name); // "sayName" var descriptor = Object.getOwnPropertyDescriptor(person, "firstName"); console.log(descriptor.get.name); // "get firstName"另外兩個特殊情況
使用 bind() 創(chuàng)建的函數(shù)會在名稱屬性值之前帶有"bound"前綴
使用 Function 構(gòu)造器創(chuàng)建的函數(shù),其名稱屬性則會有 "anonymous" 前綴
var doSomething = function() { // ... }; console.log(doSomething.bind().name); // "bound doSomething" console.log((new Function()).name); // "anonymous"明確函數(shù)的雙重用途
JS 為函數(shù)提供了兩個不同的內(nèi)部方法: [[Call]] 與 [[Construct]] 。當(dāng)函數(shù)未使用 new進(jìn)行調(diào)用時, [[call]] 方法會被執(zhí)行,運(yùn)行的是代碼中顯示的函數(shù)體。而當(dāng)函數(shù)使用 new進(jìn)行調(diào)用時, [[Construct]] 方法則會被執(zhí)行,負(fù)責(zé)創(chuàng)建一個被稱為新目標(biāo)的新的對象,并
且使用該新目標(biāo)作為 this 去執(zhí)行函數(shù)體。擁有 [[Construct]] 方法的函數(shù)被稱為構(gòu)造器。
使用instanceof
function Person(name) { if (this instanceof Person) { this.name = name; // 使用 new } else { throw new Error("You must use new with Person.") } } var person = new Person("Nicholas"); var notAPerson = Person("Nicholas"); // 拋出錯誤
但這種情況下并不可靠:
function Person(name) { if (this instanceof Person) { this.name = name; // 使用 new } else { throw new Error("You must use new with Person.") } } var person = new Person("Nicholas"); var notAPerson = Person.call(person, "Michael"); // 奏效了!new.target 元屬性
通過檢查 new.target 是否被定義,這個新的元屬性就讓你能安全地判斷函數(shù)是否被使用new進(jìn)行了調(diào)用。
function Person(name) { if (typeof new.target !== "undefined") { this.name = name; // 使用 new } else { throw new Error("You must use new with Person.") } } var person = new Person("Nicholas"); var notAPerson = Person.call(person, "Michael"); // 出錯!
也可以檢查 new.target 是否被使用特定構(gòu)造器進(jìn)行了調(diào)用,例如以下代碼:
function Person(name) { if (new.target === Person) { this.name = name; // 使用 new } else { throw new Error("You must use new with Person.") } } function AnotherPerson(name) { Person.call(this, name); } var person = new Person("Nicholas"); var anotherPerson = new AnotherPerson("Nicholas"); // 出錯!
警告:在函數(shù)之外使用 new.target 會有語法錯誤。塊級函數(shù) 嚴(yán)格模式的塊級函數(shù)
"use strict"; if (true) { // 在 ES5 會拋出語法錯誤, ES6 則不會 function doSomething() { // ... } }
塊級函數(shù)會被提升到定義所在的代碼塊的頂部:
"use strict"; if (true) { console.log(typeof doSomething); // "function" function doSomething() { // ... } doSomething(); } console.log(typeof doSomething); // "undefined"
let 函數(shù)表達(dá)式:
"use strict"; if (true) { console.log(typeof doSomething); // 拋出錯誤 let doSomething = function () { // ... } doSomething(); } console.log(typeof doSomething);非嚴(yán)格模式的塊級函數(shù)
ES6 在非嚴(yán)格模式下同樣允許使用塊級函數(shù),但行為有細(xì)微不同。塊級函數(shù)的作用域會被提升到所在函數(shù)或全局環(huán)境的頂部,而不是代碼塊的頂部。
// ES6 behavior if (true) { console.log(typeof doSomething); // "function" function doSomething() { // ... } doSomething(); } console.log(typeof doSomething); // "function"箭頭函數(shù)
箭頭函數(shù)與傳統(tǒng)的 JS 函數(shù)區(qū)別:
沒有 this 、 super 、 arguments ,也沒有 new.target 綁定
不能被使用 new 調(diào)用
沒有原型: 既然不能對箭頭函數(shù)使用 new ,那么它也不需要原型,也就是沒有prototype 屬性。
不能更改 this : this 的值在函數(shù)內(nèi)部不能被修改,在函數(shù)的整個生命周期內(nèi)其值會保持不變
沒有 arguments 對象
不允許重復(fù)的具名參數(shù)
箭頭函數(shù)語法無參數(shù)
var getName = () => "Nicholas"; // 有效等價于: var getName = function() { return "Nicholas"; };
單個參數(shù)
var reflect = value => value; // 有效等價于: var reflect = function(value) { return value; };
多個參數(shù)
var sum = (num1, num2) => num1 + num2; // 有效等價于: var sum = function(num1, num2) { return num1 + num2; };
多個函數(shù)語句體
var sum = (num1, num2) => { return num1 + num2; }; // 有效等價于: var sum = function(num1, num2) { return num1 + num2; }; //將對象字面量包裹在括號內(nèi),標(biāo)示了括號內(nèi)是一個字面量而不是函數(shù)體。 var getTempItem = id => ({ id: id, name: "Temp" }); // 有效等價于: var getTempItem = function(id) { return { id: id, name: "Temp" }; };創(chuàng)建立即調(diào)用函數(shù)表達(dá)式
傳統(tǒng)函數(shù)
let person = function(name) { return { getName: function() { return name; } }; }("Nicholas"); console.log(person.getName()); // "Nicholas"
箭頭函數(shù)
let person = ((name) => { return { getName: function() { return name; } }; })("Nicholas"); console.log(person.getName()); // "Nicholas"
譯注:使用傳統(tǒng)函數(shù)時, (function(){/函數(shù)體/})(); 與 (function(){/函數(shù)體/}());尾調(diào)用優(yōu)化
這兩種方式都是可行的。
但若使用箭頭函數(shù),則只有下面的寫法是有效的: (() => {/函數(shù)體/})();
尾調(diào)用是指在函數(shù)return的時候調(diào)用一個新的函數(shù),由于尾調(diào)用的實現(xiàn)需要存儲到內(nèi)存中,在一個循環(huán)體中,如果存在函數(shù)的尾調(diào)用,你的內(nèi)存可能爆滿或溢出。
ES6中,引擎會幫你做好尾調(diào)用的優(yōu)化工作,你不需要自己優(yōu)化,但需要滿足下面3個要求:
1、函數(shù)不是閉包
2、尾調(diào)用是函數(shù)最后一條語句
3、尾調(diào)用結(jié)果作為函數(shù)返回
一個滿足以上要求的函數(shù)如下所示:
"use strict"; function a() { return b(); }
下面的都是不滿足的寫法:
//沒有return不優(yōu)化 "use strict"; function a() { b(); } //不是直接返回函數(shù)不優(yōu)化 "use strict"; function a() { return 1 + b(); } //尾調(diào)用是函數(shù)不是最后一條語句不優(yōu)化 "use strict"; function a() { const s = b(); return s } //閉包不優(yōu)化 "use strict"; function a() { const num = 1 function b() { return num } return b }
尾調(diào)用實際用途——遞歸函數(shù)優(yōu)化
在ES5時代,我們不推薦使用遞歸,因為遞歸會影響性能。
但是有了尾調(diào)用優(yōu)化之后,遞歸函數(shù)的性能有了提升。
//新型尾優(yōu)化寫法 "use strict"; function a(n, p = 1) { if(n <= 1) { return 1 * p } let s = n * p return a(n - 1, s) } //求 1 x 2 x 3的階乘 let sum = a(3) console.log(sum) // 6
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/108417.html
摘要:最近買了深入理解的書籍來看,為什么學(xué)習(xí)這么久還要買這本書呢主要是看到核心團(tuán)隊成員及的創(chuàng)造者為本書做了序,作為一個粉絲,還是挺看好這本書能給我?guī)硪粋€新的升華,而且本書的作者也非常厲害。 使用ES6開發(fā)已經(jīng)有1年多了,以前看的是阮一峰老師的ES6教程,也看過MDN文檔的ES6語法介紹。 最近買了《深入理解ES6》的書籍來看,為什么學(xué)習(xí)ES6這么久還要買這本書呢?主要是看到Daniel A...
摘要:深入之繼承的多種方式和優(yōu)缺點深入系列第十五篇,講解各種繼承方式和優(yōu)缺點。對于解釋型語言例如來說,通過詞法分析語法分析語法樹,就可以開始解釋執(zhí)行了。 JavaScript深入之繼承的多種方式和優(yōu)缺點 JavaScript深入系列第十五篇,講解JavaScript各種繼承方式和優(yōu)缺點。 寫在前面 本文講解JavaScript各種繼承方式和優(yōu)缺點。 但是注意: 這篇文章更像是筆記,哎,再讓我...
摘要:回調(diào)函數(shù)模式類似于事件模型,因為異步代碼也會在后面的一個時間點才執(zhí)行如果回調(diào)過多,會陷入回調(diào)地獄基礎(chǔ)可以當(dāng)做是一個占位符,表示異步操作的執(zhí)行結(jié)果。函數(shù)可以返回一個,而不必訂閱一個事件或者向函數(shù)傳遞一個回調(diào)函數(shù)。 主要知識點:Promise生命周期、Promise基本操作、Promise鏈、響應(yīng)多個Promise以及集成PromiseshowImg(https://segmentfaul...
摘要:在可迭代對象上使用所有數(shù)組上的新方法與方法與方法均接受兩個參數(shù)一個回調(diào)函數(shù)一個可選值用于指定回調(diào)函數(shù)內(nèi)部的?;卣{(diào)函數(shù)可接收三個參數(shù)數(shù)組的某個元素該元素對應(yīng)的索引位置以及該數(shù)組自身。 主要知識點:創(chuàng)建數(shù)組、數(shù)組上的新方法、類型化數(shù)組showImg(https://segmentfault.com/img/bVbfWo1?w=991&h=587); 《深入理解ES6》筆記 目錄 創(chuàng)建數(shù)組...
閱讀 2760·2021-10-12 10:12
閱讀 840·2019-08-29 17:25
閱讀 2850·2019-08-29 17:24
閱讀 3321·2019-08-29 17:19
閱讀 1860·2019-08-29 15:39
閱讀 3132·2019-08-26 16:50
閱讀 2040·2019-08-26 12:17
閱讀 2764·2019-08-26 12:16