hello大家好谷扣,又見面了土全。
假期轉(zhuǎn)瞬即逝,年后開工的第一天会涎,早上是真的不想起床吖裹匙,為了不遲到閉著眼睛就穿衣服。
好啦好啦末秃,步入正題啦概页,打起精神哦!
前言
函數(shù)是所有編程語言中重要的組成部分蛔溃,在Es6出現(xiàn)之前 JavaScript的函數(shù)語法一直沒有太大的變化绰沥,從而遺留了很多問題和隱晦的做法,導(dǎo)致實現(xiàn)一些功能需要編寫很多代碼贺待。
函數(shù)形參默認(rèn)值
JavaScript函數(shù)有一個特別的地方徽曲,就是無論在函數(shù)形參里定義了多少參數(shù),都可以傳入任意數(shù)量的參數(shù)麸塞,但是有的情況下秃臣,我們的參數(shù)只是可填,這樣的話我們還在函數(shù)體呢寫一堆邏輯從而導(dǎo)致代碼冗余,還好Es6版本出現(xiàn)了函數(shù)默認(rèn)值奥此。
我們用Es5和Es6代碼來比對一下
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ù)多的話,這么寫代碼的話會造成非常冗余的稚虎,于是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ù),可以看到代碼非常簡化蠢终,上面代碼可以看到參數(shù)傳入了null序攘,對于默認(rèn)參數(shù)null也是一個合法值,這種情況下只有函數(shù)參數(shù)為undefined時才會使用默認(rèn)值寻拂。
函數(shù)參數(shù)表達式
關(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ù)的表達式不是一創(chuàng)建函數(shù)就立刻執(zhí)行的,而是當(dāng)該函數(shù)
person
被調(diào)用的時候并且沒有傳入?yún)?shù)才會執(zhí)行慌核。
上面example中距境,如果不傳參才會調(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中肮疗,第一個代碼塊的里面我們都能看的懂,只不過把之前的函數(shù)換成了變量扒接∥被酰看第二個代碼塊里的代碼,我們把nickName
參數(shù)默認(rèn)值設(shè)置成了第一個參數(shù)name
參數(shù)钾怔,這是在引用參數(shù)默認(rèn)值的時候碱呼,只允許引用前面參數(shù)的值,相當(dāng)于函數(shù)參數(shù)就是定義的變量宗侦,我們后面的變量可以訪問前面變量的愚臀,但是只限制在于當(dāng)前作用域中,這個函數(shù)形參里就是當(dāng)前作用域矾利。我們再看一個例子姑裂。
function person(name = nickName, nickName) {
console.log(name, nickName)
}
person("張三") // 張三 張三
上面example中,第一個參數(shù)默認(rèn)值是第二個參數(shù)男旗,這時運行會拋出一個錯誤舶斧,因為這時在定義第二個變量前去訪問,會造成暫時死區(qū)察皇,如果不明白暫時死區(qū)的可以去看我的上一篇文章茴厉。《一看就懂的var泽台、let、const三者區(qū)別》
函數(shù)參數(shù)默認(rèn)值對arguments的影響
當(dāng)使用函數(shù)默認(rèn)參數(shù)時矾缓,arguments對象的行為會與以往不同
Es5非嚴(yán)格模式下使用arguments
Es5非嚴(yán)格模式下怀酷,函數(shù)命名參數(shù)的變化會體現(xiàn)在arguments
對象上,arguments
獲取的是當(dāng)前函數(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ù)的變化會同步更新到arguments
對象中。當(dāng)a
參數(shù)的變化咐吼,會映射到arguments[0]
對象上。
Es5嚴(yán)格模式下使用arguments
下面我們再來看一下嚴(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
時锯茄,再次打印arguments
對象,它還是初始化值茶没。在嚴(yán)格模式下JavaScript
中取消了arguments
對象這個令人困惑的行為肌幽,無論參數(shù)如何變化,arguments
對象不再隨之改變抓半。
Es6中使用默認(rèn)參數(shù)值對arguments的影響
在Es6中喂急,如果一個函數(shù)使用了默認(rèn)參數(shù)值,那么arguments
對象的行為都將與JavaScript
中的嚴(yán)格模式下保持一致笛求。
function test(a, b = 2) {
a = 12
b = 10
console.log(arguments) // [1]
}
test(1)
上面example中廊移,arguments
對象打印出[1]
是因為arguments
對象獲取的是實參,我們可以看到實參參數(shù)就傳了一個值探入,所以arguments
對象就只有一個值狡孔。再看第二點,a
和b
的參數(shù)都改變了值蜂嗽,但是arguments
對象還是沒有改變苗膝,這就是上面說的,如果一個函數(shù)使用了默認(rèn)參數(shù)值植旧,那么arguments對象的行為都將與JavaScript中的嚴(yán)格模式下保持一致
辱揭。
處理無命名參數(shù)
在js中函數(shù)參數(shù)數(shù)量是任意的,當(dāng)傳入更少的數(shù)量病附,默認(rèn)參數(shù)的特性可以有效的簡化函數(shù)聲明的代碼问窃。當(dāng)傳入更多的數(shù)量,Es6也同樣提供了更好的方案胖喳。
Es5中獲取無命名參數(shù)
function test(a, b, c) {
console.log(arguments) // [1, 2, 3]
}
test(1, 2, 3)
上面example中泡躯,arguments
對象雖然也可以實現(xiàn)獲取所有的參數(shù),但是呢如果我們想獲取第二個參數(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中咕别,第一個代碼塊里實現(xiàn)了在Es6中獲取全部的參數(shù),可是還不滿足我們的需求写穴。那么看第二個代碼塊里的代碼就實現(xiàn)了惰拱,我們獲取第二個參數(shù)后面所有的參數(shù)。
Es6獲取無命名參數(shù)弊端
首先啊送,每一個函數(shù)只能聲明一個獲取不定參數(shù)偿短,而且只能放在函數(shù)的末尾,否則會報錯馋没。
function test(...params, a, b) {
}
test()
上面example中昔逗,會拋出錯誤,聲明了不定參數(shù)數(shù)之后篷朵,就不能繼續(xù)在后面聲明參數(shù)勾怒。
還有一點,不定參數(shù)不能定義在對象字面量的setter
中声旺,因為setter
函數(shù)只接收一個函數(shù)笔链,寫成不定參數(shù)之后就會是一個數(shù)組,這樣就會導(dǎo)致程序異常腮猖。
let obj = {
set name(...params) {
}
}
函數(shù)name屬性
在JavaScript
中所有的函數(shù)都有一個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",對應(yīng)著聲明時的函數(shù)名稱谍椅。匿名函數(shù)表達式test
函數(shù)的name
名稱误堡,對應(yīng)著被賦值為匿名函數(shù)的變量。
name屬性的特殊情況
我原來以為每個函數(shù)的name
名稱都是對應(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
是一個取值函數(shù)getter
杖们,所以它的函數(shù)名稱get getName
悉抵,如果是setter
函數(shù)的話那么名稱會有帶有前綴set
。通過bind
創(chuàng)建的函數(shù)摘完,它的名稱帶有"bound"前綴姥饰。
箭頭函數(shù)
Es6中箭頭函數(shù)是其中最有趣的特性,箭頭函數(shù)是一種使用箭頭=>
定義函數(shù)的新語法孝治,但是它與傳統(tǒng)的JavaScript
函數(shù)有些不同列粪,具體看下面幾點审磁。
- 沒有
this
、super
岂座、arguments
- 不能通過
new
關(guān)鍵字調(diào)用 - 沒有原型
prototype
- 不可以改變
this
指向 - 不支持重復(fù)的命名參數(shù)
箭頭函數(shù)和傳統(tǒng)函數(shù)一樣都有一個name屬性态蒂,這一點是不變的费什。
箭頭函數(shù)語法
let person = () => "蛙人"
// 相當(dāng)于下代碼
function person() {
return "蛙人"
}
上面example中钾恢,當(dāng)箭頭函數(shù)右側(cè)的表達式求值后會立即返回。
箭頭函數(shù)參數(shù)
let getName = val => val
// 相當(dāng)于下代碼
function getName(val) {
return val
}
當(dāng)箭頭函數(shù)只有一個參數(shù)時,就可以省略括號巡球,直接寫參數(shù)名。如果要傳入兩個或多個參數(shù)吴超,則就需要帶上括號【ㄗ瑁看下面例子
let sum = (a, b) => a + b
// 相當(dāng)于下代碼
function sun(a, b) {
return a + b
}
如果你想返回一個對象字面量鸟悴,可以這樣寫
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
對象
let person = {
test: () => {
console.log(this)
},
fn() {
return () => {
console.log(this)
}
}
}
person.test() // window
person.fn()() // person對象
上面example中利赋,可以清楚的看到箭頭沒有this
,那么它的this
只會去找外層的非箭頭函數(shù)的函數(shù)猩系。
箭頭函數(shù)沒有arguments
對象
同樣箭頭函數(shù)也沒有arguments
對象媚送,但是如果它外層還有一層非箭頭函數(shù)的話,就會去找外層的函數(shù)的arguments
對象寇甸, 如下
let test1 = () => console.log(arguments) // 執(zhí)行該函數(shù)會拋出錯誤
function test2(a, b, c) {
return () => {
console.log(arguments) // [1, 2, 3]
}
}
test2(1, 2, 3)()
上面example中塘偎,可以清楚的看到當(dāng)前的箭頭函數(shù)沒有arguments
對象疗涉,然而就去它的外層去找非箭頭函數(shù)的函數(shù)。注意:箭頭函數(shù)找arguments對象只會找外層非箭頭函數(shù)的函數(shù)吟秩,如果外層是一個非箭頭函數(shù)的函數(shù)如果它也沒有arguments對象也會中斷返回咱扣,就不會在往外層去找了
》宄ⅲ看下面例子
function test(a) {
return function() {
return () => {
console.log(arguments) // []
}
}
}
test(1)()()
上面example中可以看到偏窝,里面的箭頭函數(shù)往外層找非箭頭函數(shù)的函數(shù),然后不管外層這個函數(shù)有沒有arguments
對象都會返回武学。只有它是非箭頭函數(shù)就可以祭往,如果外層是箭頭函數(shù)還會繼續(xù)往外層找。
箭頭函數(shù)不能用new
關(guān)鍵字聲明
let test = () => {}
new test() // 拋出錯誤火窒,找不到constructor對象
箭頭函數(shù)沒有原型prototype
切記硼补,箭頭函數(shù)沒有原型,有可能面試官會問熏矿,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
指向的方法都不會拋出錯誤,但是都無效票编,都不能改變this
指向褪储。
箭頭函數(shù)不能重復(fù)命名參數(shù)
let sum = (a, a) => {} // 拋出錯誤,參數(shù)不能重復(fù)