做爲前端切圖仔,愈加以爲本身離不開函數了。前端
說到JavaScript函數,腦子裏都是匿名函數、普通函數、閉包函數、構造函數......而後還能說出一大堆函數的概念。若是你達到這個水平,那麼函數對你來講沒有難度,是一個前端老鳥了。react
當我閉上眼睛,不看鍵盤,手指在鍵盤上敲擊出一個排序函數的時候,我在想,仍是回顧一下函數的基本知識才有意思。segmentfault
在ES5中,咱們給函數傳參數,而後在函數體內設置默認值,以下面這種方式。數組
function a(num, callback) { num = num || 6 callback = callback || function (data) {console.log('ES5: ', data)} callback(num * num) } a() //ES5: 36,不傳參輸出默認值 //你還能夠這樣使用callback a(10, function(data) { console.log(data * 10) // 1000, 傳參輸出新數值 })
而在ES6中,咱們使用新的默認值寫法。閉包
function a(num = 6, callback = function (data) {console.log('ES6: ', data)}) { callback(num * num) } a() //ES6: 36, 不傳參輸出默認值 a(10, function(data) { console.log(data * 10) // 1000,傳參輸出新數值 })
使用ES6的默認值寫法可讓函數體內部的代碼更加簡潔優雅app
默認值對arguments對象的影響函數
咱們先要了解arguments對象是什麼?準確一點來講它是一個類數組對象,它存在函數內部,它將當前函數的全部參數組成了一個類數組對象。性能
function a(num, b){ console.log(arguments) // {"0": 6, "1": 10} console.log(arguments.length) // 2 } a(6, 10)
上面的輸出結果看起來很正常,那麼,若是咱們加上參數默認值會怎樣呢?學習
function a(num = 1, b = 1){
console.log(arguments)
}
a() // {} 默認值不能被arguments識別。
a(6, 10) // {"0":6,"1":10}優化
下面咱們看一下修改參數默認值對arguments的影響。
一、在ES5的非嚴格模式下,一開始輸入的參數是1,那麼能夠獲取到arguments[0](表示第一個參數)全等於num,修改num = 2以後,arguments[0]也能更新到2。
function a(num){ console.log(num === arguments[0]) //true num = 2 //修改參數默認值 console.log(num === arguments[0]) //true } a(1)
二、在ES5的非嚴格模式下,arguments就不能在函數內修改默認值後跟隨着跟新了。
"use strict"; //嚴格模式 function a(num) { console.log(num === arguments[0]); // true num = 2; console.log(num === arguments[0]); // false } a(1);
在ES6環境下,默認值對arguments的影響和ES5嚴格模式是一樣的標準。
默認參數表達式
參數不只能夠設置默認值爲字符串,數字,數組或者對象,還能夠是一個函數。
function add() { return 10 } function a(num = add()){ console.log(num) } a() // 10
默認參數的臨時死區
第一章咱們提到了let和const什麼變量的臨時死區(TDZ),默認參數既然是參數,那麼也一樣有臨時死區,函數的做用域是獨立的,a函數不能共享b函數的做用域參數。
//這是個默認參數臨時死區的例子,當初始化a時,b尚未聲明,因此第一個參數對b來講就是臨時死區。 function add(a = b, b){ console.log(a + b) } add(undefined, 2) // b is not define
上面說的參數都是命名參數,而無命名參數也是函數傳參時常常用到的。當傳入的參數是一個對象,不是一個具體的參數名,則是無命名參數。
function add(object){ console.log(object.a + object.b) } let obj = { a: 1, b: 2 } add(obj) // 3
不定參數的使用:使用...(展開運算符)的參數就是不定參數,它表示一個數組。
function add(...arr){ console.log(a + b) } let a = 1,b = 2 add(a, b) // 3
不定參數的使用限制:必須放在全部參數的末尾,不能用於對象字面量setter中。
//錯誤的寫法1 function add(...arr, c){ console.log(a + b) } let a = 1,b = 2,c = 3 add(a, b, c) //錯誤的寫法2 let obj = { set add(...arr) { } }
ES6中的構造函數Function新增了支持默認參數和不定參數。
展開運算符的做用是解構數組,而後將每一個數組元素做爲函數參數。
有了展開運算符,咱們操做數組的時候,就能夠再也不使用apply來指定上下文環境了。
//ES5的寫法 let arr = [10, 20, 50, 40, 30] let a = Math.max.apply(null, arr) console.log(a) // 50 //ES6的寫法 let arr = [10, 20, 50, 40, 30] let a = Math.max(...arr) console.log(a) // 50
嚴格模式下:在ES6中,你能夠在塊級做用域內聲明函數,該函數的做用域只限於當前塊,不能在塊的外部訪問。
"use strict"; if(true) { const a = function(){ } }
非嚴格模式:即便在ES6中,非嚴格模式下的塊級函數,他的做用域也會被提高到父級函數的頂部。因此你們寫代碼儘可能使用嚴格模式,避免這些奇葩狀況。
若是看到你這裏,你發現你尚未在項目中使用過箭頭函數,不要緊,你並不low,而是學習不夠努力。
const arr = [5, 10] const s = arr.reduce((sum, item) => sum + item) console.log(s) // 15
箭頭函數和普通函數的區別是:
一、箭頭函數沒有this,函數內部的this來自於父級最近的非箭頭函數,而且不能改變this的指向。
二、箭頭函數沒有super
三、箭頭函數沒有arguments
四、箭頭函數沒有new.target綁定。
五、不能使用new
六、沒有原型
七、不支持重複的命名參數。
箭頭函數的簡單理解
一、箭頭函數的左邊表示輸入的參數,右邊表示輸出的結果。
const s = a => a console.log(s(2)) // 2
二、箭頭函數中,最重要的this報錯將再也不成爲你天天都擔憂的bug。
三、箭頭函數還能夠輸出對象,在react的action中就推薦這種寫法。
const action = (type, a) => ({ type: "TYPE", a })
四、支持當即執行函數表達式寫法
const test = ((id) => { return { getId() { console.log(id) } } })(18) test.getId() // 18
五、箭頭函數給數組排序
const arr = [10, 50, 30, 40, 20] const s = arr.sort((a, b) => a - b) console.log(s) // [10,20,30,40,50]
尾調用是什麼鬼?
尾調用是指在函數return的時候調用一個新的函數,因爲尾調用的實現須要存儲到內存中,在一個循環體中,若是存在函數的尾調用,你的內存可能爆滿或溢出。
ES6中,引擎會幫你作好尾調用的優化工做,你不須要本身優化,但須要知足下面3個要求:
一、函數不是閉包
二、尾調用是函數最後一條語句
三、尾調用結果做爲函數返回
一個知足以上要求的函數以下所示:
"use strict"; function a() { return b(); }
下面的都是不知足的寫法:
//沒有return不優化 "use strict"; function a() { b(); } //不是直接返回函數不優化 "use strict"; function a() { return 1 + b(); } //尾調用是函數不是最後一條語句不優化 "use strict"; function a() { const s = b(); return s } //閉包不優化 "use strict"; function a() { const num = 1 function b() { return num } return b }
尾調用實際用途——遞歸函數優化
在ES5時代,咱們不推薦使用遞歸,由於遞歸會影響性能。
可是有了尾調用優化以後,遞歸函數的性能有了提高。
//新型尾優化寫法 "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
函數這一章涉及到的知識點比較多,默認參數,命名參數,不定參數,展開運算符,箭頭函數,尾調用優化。
第一次學習這些知識的人能夠關注箭頭函數和展開運算符的使用,這是最重要也最經常使用的知識,若是你已經在項目中使用過這些知識,那麼做爲鞏固也是有幫助的,俗話說溫故知新。