天天看點

前端面試系列-JavaScript-箭頭函數(與普通函數的差別)一、箭頭函數二、與普通函數的差別

文章目錄

  • 一、箭頭函數
  • 二、與普通函數的差別
    • 1.箭頭函數不會建立自己的this
    • 2.箭頭函數繼承而來的this指向永遠不變(重要)
    • 3.call()、apply()、bind()無法改變箭頭函數中this的指向
    • 4.箭頭函數不綁定arguments,取而代之用rest參數…解決
    • 5.不能使用new操作符(作為構造函數使用)
    • 6.不能使用原型屬性
    • 7.不能簡單傳回對象字面量
    • 8.箭頭函數不能換行

一、箭頭函數

// 箭頭函數
let fun = (name) => {
    // 函數體
    return `Hello ${name} !`;
};
           
// 等同于
let fun = function (name) {
    // 函數體
    return `Hello ${name} !`;
};
           

二、與普通函數的差別

1.箭頭函數不會建立自己的this

箭頭函數沒有自己的this,它會捕獲自己在定義時(注意,是定義時,不是調用時)所處的外層執行環境的this,并繼承這個this值。是以,箭頭函數中this的指向在它被定義的時候就已經确定了,之後永遠不會改變。

來看個例子:

var id = 'Global';

function fun1() {
    // setTimeout中使用普通函數
    setTimeout(function(){
        console.log(this.id);
    }, 2000);
}

function fun2() {
    // setTimeout中使用箭頭函數
    setTimeout(() => {
        console.log(this.id);
    }, 2000)
}

fun1.call({id: 'Obj'});     // 'Global'

fun2.call({id: 'Obj'});     // 'Obj'
           
  • 函數fun1中的setTimeout中使用普通函數,2秒後函數執行時,這時函數其實是在全局作用域執行的,是以this指向Window對象,this.id就指向全局變量id,是以輸出’Global’。
  • 但是函數fun2中的setTimeout中使用的是箭頭函數,這個箭頭函數的this在定義時就确定了,它繼承了它外層fun2的執行環境中的this,而fun2調用時this被call方法改變到了對象{id: ‘Obj’}中,是以輸出’Obj’。

再來看另一個例子:

var id = 'GLOBAL';
var obj = {
  id: 'OBJ',
  a: function(){
    console.log(this.id);
  },
  b: () => {
    console.log(this.id);
  }
};

obj.a();    // 'OBJ'
obj.b();    // 'GLOBAL'
           
  • 對象obj的方法a使用普通函數定義的,普通函數作為對象的方法調用時,this指向它所屬的對象。是以,this.id就是obj.id,是以輸出’OBJ’。
  • 但是方法b是使用箭頭函數定義的,箭頭函數中的this實際是繼承它定義時的外層執行環境的this,它定義時所處的外層執行環境就是全局環境,是以指向Window對象,是以輸出’GLOBAL’。(定義對象的大括号{}是無法形成一個單獨的執行環境的,它依舊是處于全局執行環境中)

2.箭頭函數繼承而來的this指向永遠不變(重要)

箭頭函數繼承而來的this指向永遠不變。對象obj的方法b是使用箭頭函數定義的,這個函數中的this就永遠指向它定義時所處的全局執行環境中的this,即便這個函數是作為對象obj的方法調用,this依舊指向Window對象。

3.call()、apply()、bind()無法改變箭頭函數中this的指向

由于 this 已經在詞法層面完成了綁定,通過 call() 、 apply() 、bind()方法調用一個函數時,隻是傳入了參數而已,對 this 并沒有什麼影響:

var id = 'Global';

// 箭頭函數定義在全局作用域
let fun1 = () => {
    console.log(this.id)
};

fun1();     // 'Global'
// this的指向不會改變,永遠指向Window對象
fun1.call({id: 'Obj'});     // 'Global'
fun1.apply({id: 'Obj'});    // 'Global'
fun1.bind({id: 'Obj'})();   // 'Global'
           

4.箭頭函數不綁定arguments,取而代之用rest參數…解決

箭頭函數沒有自己的arguments對象。在箭頭函數中通路arguments實際上獲得的是外層局部(函數)執行環境中的值。

// 例子一
let fun = (val) => {
    console.log(val);   // 111
    console.log(arguments); 
        // Uncaught ReferenceError: arguments is not defined
    // 因為外層全局環境沒有arguments對象
};
fun(111);
           
// 例子二
function outer(val1, val2) {
    let argOut = arguments;
    console.log(argOut);    //Arguments(2) [111, 222, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    let fun = () => {
        let argIn = arguments;
        console.log(argIn);  //Arguments(2) [111, 222, callee: ƒ, Symbol(Symbol.iterator): ƒ]
        console.log(argOut === argIn);  // true,箭頭函數中通路arguments是外層局部(函數)執行環境中的值。
    };
    fun();
}
outer(111, 222);
           

rest參數用于擷取函數的多餘參數,這樣就不需要使用arguments對象了

var foo = (...args) => {
  return args[0]
}
console.log(foo(1))    //1
           

5.不能使用new操作符(作為構造函數使用)

箭頭函數不能用作構造器,和 new 一起用就會抛出錯誤。

var Foo = () => {};
var foo = new Foo();  //Foo is not a constructor
           

構造函數的new操作分為四步:

  1. 建立一個空對象
  2. 連結到原型
  3. 綁定this值
  4. 傳回新對象

但是,箭頭函數沒有原型屬性,沒有自己的this,它的this其實是繼承了外層執行環境中的this,且this指向永遠不會随在哪裡調用、被誰調用而改變,是以箭頭函數不能作為構造函數使用,或者說構造函數不能定義成箭頭函數,否則用new調用時會報錯。

6.不能使用原型屬性

箭頭函數沒有原型屬性。

var foo = () => {};
console.log(foo.prototype) //undefined
           

7.不能簡單傳回對象字面量

var func = () => {  foo: 1  };
// Calling func() returns undefined!
var func = () => {  foo: function() {}  };
// SyntaxError: function statement requires a name
           

如果要傳回對象字面量,用括号包裹字面量

var func = () => ({ foo: 1 });
           

8.箭頭函數不能換行

var func = ()
           => 1; // SyntaxError: expected expression, got '=>'
           

參考:https://www.jianshu.com/p/eca50cc933b7

本文連結:https://blog.csdn.net/qq_39903567/article/details/115208019

繼續閱讀