箭頭函式、簡寫函式、普通函式的區別

crayons發表於2019-12-23

ES6新增了很多新特性,箭頭函式、簡化函式是我們接觸的最多的特性了,但是它們與傳統的Javascript函式(本文稱之為普通函式)有些不同

一、箭頭函式

  • 沒有this、super、arguments和new.target繫結
  • 不能通過new關鍵字呼叫
  • 沒有原型
  • 不可以改變this的繫結
  • 不支援arguments物件
  • 不支援重複的命名引數
  • 不支援詞法名稱識別符號

1. 沒有this、super、arguments和new.target繫結

箭頭函式中的this、super、arguments及new.target這些值由外圍最近一層非箭頭函式決定,它本身沒有這些繫結,下面將用一些例子進行說明:

1.1、沒有this繫結

var myObject = {
    age:30,
    run:()=>{
        console.log('張三今年'+this.age+'歲');   
    }
}
myObject.run(); // 非嚴格模式下,列印出:張三今年undefined歲

var myObject2 = {
    age:30,
    run:function(){
        setTimeout(()=>{
            console.log('張三今年'+this.age+'歲')
        },0)
    }
}
myObject2.run();   // 張三今年30歲
複製程式碼

上面第一個例子,直接在物件中定義了一個箭頭函式方法,結果為undefiend,通過查詢我們會發現,此時在非嚴格模式下的this指向了全域性物件(window),第二個例子,我們在物件中定義了一個function函式,然後在函式裡面使用了箭頭函式,此時得到的結果是正常的,這說明箭頭函式本身並沒有this,this繫結由外層非箭頭函式決定的,示例2等價於下面程式碼:

var myObject2 = {
    age:30,
    run:function(){
        var that = this;
        setTimeout(function(){
            console.log('張三今年'+that.age+'歲');
        },0)
    }
}
myObject2.run();   // 張三今年30歲
複製程式碼

1.2、沒有super

var foo = {
    run:()=>{
        console.log(super.name);
    }
}
var person = {
    name:'張三'
}
Object.setPrototypeOf(foo,person);
foo.run();  // Uncaught SyntaxError: 'super' keyword unexpected here
複製程式碼

1.3、沒有arguments物件

var person = ()=>{
    console.log(arguments[0]);
}
person(30);    // Uncaught ReferenceError: arguments is not defined
複製程式碼

1.4、沒有new.target元屬性

var person = ()=>{
    console.log(new.target);
}
person(); // Uncaught SyntaxError: new.target expression is not allowed here
複製程式碼

2、不能通過new關鍵字呼叫

箭頭函式沒有[[Construct]]方法,所以不能被用作建構函式,如果通過new關鍵字呼叫箭頭函式,程式會丟擲錯誤。

    var Person = (age)=>{
        this.age = age;
    }
    var obj = new Person(30); // Uncaught TypeError: Person is not a constructor
複製程式碼

3、沒有原型

由於不可以通過new關鍵字呼叫箭頭函式,因而沒有構建原型的需求,所以箭頭函式不存在prototype這個屬性。

var Person = (age)=>{
    this.age = age;
}
console.log(Person.prototype);  // undefined
複製程式碼

4、不可以改變this的繫結

由於箭頭函式沒有自己的this,所以使用call/bind/apply等方法無法改變this的繫結

var obj = {
	age:'唐華'
}
var foo = ()=>{
    console.log(this.age);
}
foo.call(obj);  // undefined
複製程式碼

5、不支援arguments物件

箭頭函式沒有arguments繫結,所以只能通過命名引數或者不定引數(...)這兩種形式訪問函式的引數。


var foo = ()=>{
    console.log(arguments[0]);
}
foo(10);  // Uncaught ReferenceError: arguments is not defined
複製程式碼

6、不支援重複的命名引數

無論在嚴格還是非嚴格模式下,箭頭函式都不支援重複的命名引數。

var foo = (name,name)=>{
    console.log(name,name);
}
foo(10,20);  // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
複製程式碼

7、不支援詞法名稱識別符號

箭頭函式不能定義詞法名稱識別符號,導致函式自我引用(遞迴、事件(解除)繫結)更難操作。

var Foo = {
    // Uncaught SyntaxError: Malformed arrow function parameter list
    bar:bar()=> {              
    		console.log('箭頭函式');  
    },
    baz: function baz() {
    		console.log('普通函式');
    }
};
複製程式碼

二、簡寫函式

  • super繫結
  • 不支援詞法名稱識別符號

1、super繫結

super關鍵字用於訪問和呼叫一個物件的父物件上的函式。

class Person{
	constructor(){
		this.age = '30'
	}
    run(){
        console.log(this.age)
    }
}

class Foo extends Person{
    constructor(){
        super();
    	this.age = '40';
    }
    sayName(){
        super.run();
    }
}
var obj = new Foo();
obj.sayName();   
複製程式碼

上面的例子使用extends實現繼承,在子類的constructor中必須呼叫super方法,因為子類沒有自己的 this 物件,需要繼承父類的this物件,子類中的super就代表了父類的建構函式,呼叫super(...)後子類中的this指向的是子類的例項,即super內部的this指的是Foo,相當於:Person.prototype.constructor.call(this)。

注意:super在靜態方法之中指向父類,在普通方法之中指向父類的原型物件

2、不支援詞法名稱識別符號

簡寫函式不能像普通函式一樣定義詞法名稱識別符號,導致函式自我引用(遞迴、事件(解除)繫結)更難操作。

var Foo = {
    bar(){              
        console.log('簡寫函式');  
    },
    baz: function baz() {
        console.log('普通函式');
    }
};
複製程式碼

三、普通函式

  • 沒有super繫結

普通函式並沒有super繫結,只有簡寫函式才有。

var foo = {
    name:'tanghua'
}
var bar = {
    run:function(){
        console.log(super.name);
    }
}
Object.setPrototypeOf(bar,foo);
bar.run();     // Uncaught SyntaxError: 'super' keyword unexpected here
複製程式碼

上面的示例呼叫super關鍵字會丟擲一個錯誤,因為super只能在簡寫函式中呼叫。

相關文章