箭头函数、简写函数、普通函数的区别

1,190 阅读4分钟

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只能在简写函数中调用。