你真的了解ES6函數(shù)特性么?

hello大家好,又見面了。

假期轉(zhuǎn)瞬即逝,年后開工的第一天,早上是真的不想起床吖,為了不遲到閉著眼睛就穿衣服。

好啦好啦,步入正題啦,打起精神哦!

前言

函數(shù)是所有編程語言中重要的組成部分,在Es6出現(xiàn)之前 JavaScript的函數(shù)語法一直沒有太大的變化,從而遺留了很多問題和隱晦的做法,導(dǎo)致實(shí)現(xiàn)一些功能需要編寫很多代碼。

函數(shù)形參默認(rèn)值

JavaScript函數(shù)有一個(gè)特別的地方,就是無論在函數(shù)形參里定義了多少參數(shù),都可以傳入任意數(shù)量的參數(shù),但是有的情況下,我們的參數(shù)只是可填,這樣的話我們還在函數(shù)體呢寫一堆邏輯從而導(dǎo)致代碼冗余,還好Es6版本出現(xiàn)了函數(shù)默認(rèn)值。

我們用Es5和Es6代碼來比對(duì)一下

Es5處理默認(rèn)參數(shù)

function person(name, age) {
    name = typeof(name) != "undefined" ? name : `蛙人${+ new Date()}`
    age = typeof(age) != "undefined" ? age : 24
    
}
person()

上面example中是Es5這樣處理默認(rèn)參數(shù)值的,假如我們參數(shù)多的話,這么寫代碼的話會(huì)造成非常冗余的,于是Es6就出現(xiàn)函數(shù)參數(shù)默認(rèn)值。

Es6處理默認(rèn)參數(shù)

function person(name = "蛙人", age = 24) {
    console.log(name, age)
}
person()  // 蛙人 24
person("張三", 30) // 張三 30
person(null, null) // null null

上面example是Es6中處理的默認(rèn)參數(shù),可以看到代碼非常簡(jiǎn)化,上面代碼可以看到參數(shù)傳入了null,對(duì)于默認(rèn)參數(shù)null也是一個(gè)合法值,這種情況下只有函數(shù)參數(shù)為undefined時(shí)才會(huì)使用默認(rèn)值。

函數(shù)參數(shù)表達(dá)式

關(guān)于默認(rèn)參數(shù)值,最有趣的特性可能就是非原始值傳參了,也可以把默認(rèn)參數(shù)定義為函數(shù)or 變量

function defaultName() {
    return "蛙人"
}
function person(name = defaultName()) {
    console.log(name)
}
person("張三") // 張三
person() // 蛙人

需要注意的是,默認(rèn)參數(shù)的表達(dá)式不是一創(chuàng)建函數(shù)就立刻執(zhí)行的,而是當(dāng)該函數(shù)person被調(diào)用的時(shí)候并且沒有傳入?yún)?shù)才會(huì)執(zhí)行。

上面example中,如果不傳參才會(huì)調(diào)用默認(rèn)值的defaultName函數(shù)。

下面來看一下默認(rèn)參數(shù)傳入變量。

let defaultName = "蛙人"
function person(name = defaultName) {
     console.log(name)
}
person("張三") // 張三
person() // 蛙人
function person(name, nickName = name) {
     console.log(name, nickName)
}
person("張三") // 張三 張三
person("蛙人", "掘金蛙人") // 蛙人 掘金蛙人

上面example中,第一個(gè)代碼塊的里面我們都能看的懂,只不過把之前的函數(shù)換成了變量??吹诙€(gè)代碼塊里的代碼,我們把nickName參數(shù)默認(rèn)值設(shè)置成了第一個(gè)參數(shù)name參數(shù),這是在引用參數(shù)默認(rèn)值的時(shí)候,只允許引用前面參數(shù)的值,相當(dāng)于函數(shù)參數(shù)就是定義的變量,我們后面的變量可以訪問前面變量的,但是只限制在于當(dāng)前作用域中,這個(gè)函數(shù)形參里就是當(dāng)前作用域。我們?cè)倏匆粋€(gè)例子。

function person(name = nickName, nickName) {
     console.log(name, nickName)
}
person("張三") // 張三 張三

上面example中,第一個(gè)參數(shù)默認(rèn)值是第二個(gè)參數(shù),這時(shí)運(yùn)行會(huì)拋出一個(gè)錯(cuò)誤,因?yàn)檫@時(shí)在定義第二個(gè)變量前去訪問,會(huì)造成暫時(shí)死區(qū),如果不明白暫時(shí)死區(qū)的可以去看我的上一篇文章。《一看就懂的var、let、const三者區(qū)別》

函數(shù)參數(shù)默認(rèn)值對(duì)arguments的影響

當(dāng)使用函數(shù)默認(rèn)參數(shù)時(shí),arguments對(duì)象的行為會(huì)與以往不同

Es5非嚴(yán)格模式下使用arguments

Es5非嚴(yán)格模式下,函數(shù)命名參數(shù)的變化會(huì)體現(xiàn)在arguments對(duì)象上,arguments獲取的是當(dāng)前函數(shù)的實(shí)參,arguments在非嚴(yán)格模式下它跟形參是映射關(guān)系,就是形參有變化arguments跟著變。

function test(a, b) {
    console.log(a == arguments[0]) // true
    console.log(b == arguments[1]) // true
    a = "a"
    b = "b"
    console.log(arguments) // ["a", "b"]
}
test(1, 2)

上面example中,在非嚴(yán)格模式下,命名參數(shù)的變化會(huì)同步更新到arguments對(duì)象中。當(dāng)a參數(shù)的變化,會(huì)映射到arguments[0]對(duì)象上。

Es5嚴(yán)格模式下使用arguments

下面我們?cè)賮砜匆幌聡?yán)格模式下的arguments

 function test(a, b) {
    'use strict';

    console.log(arguments)  // [1, 2]
    b = 10
    console.log(arguments) // [1, 2]
}
test(1, 2)

上面example是嚴(yán)格模式下的,可以看出當(dāng)我們改變參數(shù)b時(shí),再次打印arguments對(duì)象,它還是初始化值。在嚴(yán)格模式下JavaScript中取消了arguments對(duì)象這個(gè)令人困惑的行為,無論參數(shù)如何變化,arguments對(duì)象不再隨之改變。

Es6中使用默認(rèn)參數(shù)值對(duì)arguments的影響

在Es6中,如果一個(gè)函數(shù)使用了默認(rèn)參數(shù)值,那么arguments對(duì)象的行為都將與JavaScript中的嚴(yán)格模式下保持一致。

 function test(a, b = 2) {
    a = 12
    b = 10
    console.log(arguments) // [1]
}
test(1)

上面example中,arguments對(duì)象打印出[1]是因?yàn)?code>arguments對(duì)象獲取的是實(shí)參,我們可以看到實(shí)參參數(shù)就傳了一個(gè)值,所以arguments對(duì)象就只有一個(gè)值。再看第二點(diǎn),ab的參數(shù)都改變了值,但是arguments對(duì)象還是沒有改變,這就是上面說的,如果一個(gè)函數(shù)使用了默認(rèn)參數(shù)值,那么arguments對(duì)象的行為都將與JavaScript中的嚴(yán)格模式下保持一致。

處理無命名參數(shù)

在js中函數(shù)參數(shù)數(shù)量是任意的,當(dāng)傳入更少的數(shù)量,默認(rèn)參數(shù)的特性可以有效的簡(jiǎn)化函數(shù)聲明的代碼。當(dāng)傳入更多的數(shù)量,Es6也同樣提供了更好的方案。

Es5中獲取無命名參數(shù)

function test(a, b, c) {
    console.log(arguments) // [1, 2, 3]
}
test(1, 2, 3)

上面example中,arguments對(duì)象雖然也可以實(shí)現(xiàn)獲取所有的參數(shù),但是呢如果我們想獲取第二個(gè)參數(shù)之后的所有參數(shù),那么還得循環(huán)去排除。

Es6中獲取無命名參數(shù)

function test(...parmas) {
    console.log(params) // [1, 2, 3, 4]
}
test(1, 2, 3, 4)
function test(a, b, ...params) {
    console.log(params)
}
test(1, 2, 3, 4)

上面example中,第一個(gè)代碼塊里實(shí)現(xiàn)了在Es6中獲取全部的參數(shù),可是還不滿足我們的需求。那么看第二個(gè)代碼塊里的代碼就實(shí)現(xiàn)了,我們獲取第二個(gè)參數(shù)后面所有的參數(shù)。

Es6獲取無命名參數(shù)弊端

首先,每一個(gè)函數(shù)只能聲明一個(gè)獲取不定參數(shù),而且只能放在函數(shù)的末尾,否則會(huì)報(bào)錯(cuò)。

function test(...params, a, b) {
    
}
test()

上面example中,會(huì)拋出錯(cuò)誤,聲明了不定參數(shù)數(shù)之后,就不能繼續(xù)在后面聲明參數(shù)。

還有一點(diǎn),不定參數(shù)不能定義在對(duì)象字面量的setter中,因?yàn)?code>setter函數(shù)只接收一個(gè)函數(shù),寫成不定參數(shù)之后就會(huì)是一個(gè)數(shù)組,這樣就會(huì)導(dǎo)致程序異常。

let obj = {
  set name(...params) {

  }
}

函數(shù)name屬性

JavaScript中所有的函數(shù)都有一個(gè)name屬性,該屬性保存的是該函數(shù)名稱的字符串。沒有名稱的函數(shù)也仍然有name屬性,該name屬性值為空字符串。

function person() {}
let test = function() {}

console.log(person.name) // person
console.log(test.name) // test

上面example中,person函數(shù)name屬性值為"person",對(duì)應(yīng)著聲明時(shí)的函數(shù)名稱。匿名函數(shù)表達(dá)式test函數(shù)的name名稱,對(duì)應(yīng)著被賦值為匿名函數(shù)的變量。

name屬性的特殊情況

我原來以為每個(gè)函數(shù)的name名稱都是對(duì)應(yīng)著當(dāng)前的函數(shù)名,后來發(fā)現(xiàn)并不是這么回事。下面來看一下函數(shù)的特殊情況

var person = {
    get getName() {
        return "蛙人"
    }
}
console.log(Object.getOwnPropertyDescriptor(person, 'getName').get.name)  // get getName

function test() {}
console.log(test.bind().name) // bound test

上面example中,person.getName是一個(gè)取值函數(shù)getter,所以它的函數(shù)名稱get getName,如果是setter函數(shù)的話那么名稱會(huì)有帶有前綴set。通過bind創(chuàng)建的函數(shù),它的名稱帶有"bound"前綴。

箭頭函數(shù)

Es6中箭頭函數(shù)是其中最有趣的特性,箭頭函數(shù)是一種使用箭頭=>定義函數(shù)的新語法,但是它與傳統(tǒng)的JavaScript函數(shù)有些不同,具體看下面幾點(diǎn)。

  • 沒有this、super、arguments
  • 不能通過new關(guān)鍵字調(diào)用
  • 沒有原型prototype
  • 不可以改變this指向
  • 不支持重復(fù)的命名參數(shù)

箭頭函數(shù)和傳統(tǒng)函數(shù)一樣都有一個(gè)name屬性,這一點(diǎn)是不變的。

箭頭函數(shù)語法

let person = () => "蛙人"

// 相當(dāng)于下代碼

function person() {
    return "蛙人"
}

上面example中,當(dāng)箭頭函數(shù)右側(cè)的表達(dá)式求值后會(huì)立即返回。

箭頭函數(shù)參數(shù)

let getName = val => val

// 相當(dāng)于下代碼

function getName(val) {
    return val
}

當(dāng)箭頭函數(shù)只有一個(gè)參數(shù)時(shí),就可以省略括號(hào),直接寫參數(shù)名。如果要傳入兩個(gè)或多個(gè)參數(shù),則就需要帶上括號(hào)??聪旅胬?/p>

let sum = (a, b) => a + b

// 相當(dāng)于下代碼

function sun(a, b) {
    return a + b
}

如果你想返回一個(gè)對(duì)象字面量,可以這樣寫

let getObj = () => ({name: "蛙人", age: 24}) // {name: "蛙人", age: 24}

// 相當(dāng)于下代碼

function getObj() {
    return {
        name: "蛙人",
        age: 24
    }
}

箭頭函數(shù)沒有this

箭頭函數(shù)的this值,取決于函數(shù)外部非箭頭函數(shù)的this值,如果上一層還是箭頭函數(shù),那就繼續(xù)往上找,如果找不到那么this就是window對(duì)象

let person = {
    test: () => {
        console.log(this)
    },
    fn() {
        return () => {
            console.log(this)
        }
    }
}
person.test()  // window
person.fn()()  // person對(duì)象

上面example中,可以清楚的看到箭頭沒有this,那么它的this只會(huì)去找外層的非箭頭函數(shù)的函數(shù)。

箭頭函數(shù)沒有arguments對(duì)象

同樣箭頭函數(shù)也沒有arguments對(duì)象,但是如果它外層還有一層非箭頭函數(shù)的話,就會(huì)去找外層的函數(shù)的arguments對(duì)象, 如下

let test1 = () => console.log(arguments)  // 執(zhí)行該函數(shù)會(huì)拋出錯(cuò)誤


function test2(a, b, c) {
    return () => {
        console.log(arguments) // [1, 2, 3]
    }
}
test2(1, 2, 3)()

上面example中,可以清楚的看到當(dāng)前的箭頭函數(shù)沒有arguments對(duì)象,然而就去它的外層去找非箭頭函數(shù)的函數(shù)。注意:箭頭函數(shù)找arguments對(duì)象只會(huì)找外層非箭頭函數(shù)的函數(shù),如果外層是一個(gè)非箭頭函數(shù)的函數(shù)如果它也沒有arguments對(duì)象也會(huì)中斷返回,就不會(huì)在往外層去找了??聪旅胬?/p>

function test(a) {
    return function() {
        return () => {
            console.log(arguments) // []
        }
    }
}
test(1)()()

上面example中可以看到,里面的箭頭函數(shù)往外層找非箭頭函數(shù)的函數(shù),然后不管外層這個(gè)函數(shù)有沒有arguments對(duì)象都會(huì)返回。只有它是非箭頭函數(shù)就可以,如果外層是箭頭函數(shù)還會(huì)繼續(xù)往外層找。

箭頭函數(shù)不能用new關(guān)鍵字聲明

let test = () => {}
new test() // 拋出錯(cuò)誤,找不到constructor對(duì)象

箭頭函數(shù)沒有原型prototype

切記,箭頭函數(shù)沒有原型,有可能面試官會(huì)問,JavaScript中所有的函數(shù)都有prototype屬性嗎

let test = () => {}
test.prototype // undefined

箭頭函數(shù)不能改變this指向

let person = {}
let test = () => console.log(this)

test.bind(person)()
test.call(person)
test.apply(person)

上面example中,改變this指向的方法都不會(huì)拋出錯(cuò)誤,但是都無效,都不能改變this指向。

箭頭函數(shù)不能重復(fù)命名參數(shù)

let sum = (a, a) => {} // 拋出錯(cuò)誤,參數(shù)不能重復(fù)

覺得寫的不錯(cuò)那就點(diǎn)個(gè)贊叭!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容