箭頭函式詳解

兔子先森發表於2023-02-22

定義箭頭函式

ES6允許使用箭頭 => 定義函式

let v = 100

// 箭頭函式
let f = a => v;

// 等同於
let f2 = function (a){
 return v
}

// 等同於
let f3 = (a) => { return v }

console.log(f());  // 100
console.log(f2()); // 100
console.log(f3()); // 100


箭頭函式引數

箭頭函式中,使用圓括號()代表引數的部分

// 無引數
let a1 = () => 5;

// 等同於
let a2 = function () {return 5}
// 有引數
let a3 = (num1,num2) => num1 + num2

// 等同於
let a4 = function (num1,num2) { return num1 + num2}

如果箭頭函式中的程式碼塊為一句話,可以省略大括號。
如果箭頭函式中的程式碼塊多於一句,需要使用大括號括起來,使用return可以返回資料。


大括號被解釋為程式碼塊,如果箭頭函式返回的是一個物件,就必須在物件外面加上括號,否則報錯

// 錯誤寫法
let b1 = id => { id : id , name : '東方不敗'}  // 報錯
// 正確寫法
let id = 1
let b2 = id => ({id,name:'東方不敗'})
console.log(b2()); // id : undefined ,name :'東方不敗'

上面這個程式碼塊中,箭頭函式引數為id,呼叫b2函式時未傳引數,所以idundefined,引數部分其實是定義了一個let id;id並沒有賦值,所以是undefined


在普通函式中,函式沒有引數且函式內部沒有定義變數,內部的變數會向上尋找變數,所以這裡內部returnid指向的是外層全域性變數id

let id = 1
function b3(){
    return {id,name:'東方求敗'}
    } 
console.log(b3());  // id : 1 ,name :'東方不敗'

如果函式有引數,但是呼叫函式時未傳參,那麼該引數就是undefined

// 這裡有一個引數,但是呼叫的時候沒有傳參,所以id為undefined
function b4(id){
    return {id,name:'東方求敗'}
 }
console.log(b4());  // id : undefined ,name :'東方不敗'


函式引數部分其實是有一個作用域的,引數部分的id其實是定義了一個let id,下面這個例子就能說明:

// 報錯
let id = 1
function b5(id = id){
    return {id,name:'東方求敗'}
}
b5()

這裡的id其實就是let id = id ,id為一個未宣告的變數,賦值id = id,報錯

// 呼叫函式不攜帶引數獲取外層全域性變數的正確方法
let id = 1
function b5(val = id){
    return {val,name:'東方求敗'}
  }
console.log(b5());  // id : 1 ,name :'東方不敗'


箭頭函式返回值

如果箭頭函式只有一句話且沒有返回值,可以在內容前面加void,不需要寫大括號,直接寫內容即可

let fooEmpty = () => void '無返回值';
console.log('無:',fooEmpty());   // 無: undefined

箭頭函式有返回值

let fooHave = () => '有返回值';
console.log('有:',fooHave());  // 有: 有返回值


箭頭函式大括號

let foo = () => {a : 1}
console.log(foo());  // undefined

上面例子是一種特殊情況,雖然可以執行,但的到的結果是錯誤的,因為大括號在箭頭函式中被認為是程式碼塊,這裡的箭頭函式並不是return了一個{a:1}的物件,而是執行了一個表示式 a : 1,表示式執行完成後結束執行,沒有返回值。
正常的寫法如下:

let foo2 = () => { return {a : 1} }
console.log(foo2());  // {a : 1}


箭頭函式解構賦值

在引數部分傳入物件,解構賦值

let foo3val = {id:1, name:'孫悟空'}
let foo3 = ({id,name}) => console.log(`id:${id},名稱:${name}`);
foo3(foo3val)  // id:1,名稱:孫悟空

有了解構賦值後,箭頭函式可以使表達更簡潔、更加直觀,例如這裡封裝一個簡單的工具函式,一行程式碼完成求奇偶的工具函式封裝

const isEven = n => n % 2 === 0

console.log(isEven(2));  // true
console.log(isEven(3));  // false


簡化回撥函式

普通函式寫法

let fil = [1,2,3].filter(function(x){
   return x == 2
})
console.log(fil); // [2]

箭頭函式寫法,一行搞定

let fil2 = [1,2,3].filter(el=> el == 2 )
console.log(fil2); // [2]


箭頭函式的注意點

1、箭頭函式沒有this物件
2、不可以當做建構函式,也就是說,不可以對箭頭函式使用new命令,否則報錯
3、不能使用arguments物件,該物件在函式體內不存在
4、不能使用yield命令,因為箭頭函式不能用作Generator函式

箭頭函式導致this總是指向函式定義生效時所在的物件


箭頭函式this指向

在箭頭函式的使用注意點中,最需要注意的就是this指向問題,這裡舉一個例子說明箭頭函式this指向的問題

function arrows(){

    console.log('name:',this.name);     // 中國工藝美術史
    
    setTimeout(()=>{
       console.log('name:', this.name);  // 中國工藝美術史
    },100)

    setTimeout(function(){
        console.log('name:', this.name); // 藝術概論
    },100)
 }
    
   var name = '藝術概論'
   arrows.call({name : '中國工藝美術史'})  // name: 中國工藝美術史
call函式:
第一個引數列示:想讓this指向的物件(obj)
第二個引數列示:傳進去的實參
如果call的第一個引數未傳值,或者傳值為null,那麼arrowsthis指向為window
這裡傳入第一個傳引數,this指向obj物件

第一個定時器setTimeout使用了箭頭函式,在普通函式中,setTimeout指向的是window,但這裡setTimeout使用了箭頭函式,導致定時器指向的是呼叫函式定義生效時所在的物件,所以這裡列印的是 中國工藝美術史,而不是最外層全域性變數 藝術概論
第二個定時器setTimeout沒有使用箭頭函式,即使使用call()方法,在函式內部的定時器還是指向window

箭頭函式沒有自己的this,導致內部的this就是外層程式碼塊的this。正是因為沒有this,所以也就不能用作建構函式。

箭頭函式裡沒有自己的this,而是引用外層的this。下面是 Babel 轉箭頭函式產生的 ES5 程式碼,就能清楚地說明this的指向。

// ES6
function foo() {
    setTimeout(() => {
        console.log('id:', this.id);
    }, 100);
}

// ES5
function foo() {
    var _this = this;
    
    setTimeout(function () {
        console.log('id:', _this.id);
    }, 100);
} 


箭頭函式不適用的場合

1:物件內定義箭頭函式

let cat = {
    level : 9,
    jumps : ()=> {
      this.level
    }
}

在這個例子中,cat.jumps()是一個箭頭函式,這是錯誤的。如果cat.jumps()是一個普通函式, 該方法內部的this指向cat。如果呼叫cat.jumps()是一個箭頭函式時,this指向全域性物件。因為物件不構成單獨的作用域,導致jumps箭頭函式定義時的作用域是全域性作用域。

2:需要動態this的時候,不應該使用箭頭函式

let button = document.getElementById('news')

button.addEventListener('click',()=>{
    this.classList.toggle('on')
})

上面程式碼執行時,點選按鈕報錯,因為button監聽函式是一個箭頭函式,導致內部的this是全域性物件。如果改成普通函式,this就會動態指向被點選的按鈕物件。

另外,如果函式體很複雜,有許多行,或者函式內部有大量的讀寫操作,不單純是為了計算值,這時也不應該使用箭頭函式,而是要使用普通函式,這樣可以提高程式碼可讀性。


案例原始碼:https://gitee.com/wang_fan_w/es6-science-institute

如果覺得這篇文章對你有幫助,歡迎點亮一下star喲

相關文章