ES6学习笔记:箭头函数

编辑推荐: 掘金是一个高质量的技术社区,从 CSS 到 Vue.js,性能优化到开源类库,让你不错过前端开发的每一个技术干货。 点击链接查看最新前端内容,或到各大应用市场搜索「 掘金」下载APP,技术干货尽在掌握中。

前端的学习是永无止境的,总是不断的有新知识需要学习,为了能跟上节奏,也硬逼自己开始学习一些ES6相关的知识。今天先来学习ES6中的箭头函数。在学习箭头函数之前先简单的回顾一下JavaScript中的一些概念。

函数常见的写法及调用方法

平时在看别人写的代码,总是能看到各种不同风格的JavaScript函数写法,自己一脸蒙逼。写个函数还有这么多的姿势。对于新手更是蛋疼,那么在JavaScript中常见的一些写法有:

常规写法

这种方法也称之为函数声明。

// 函数的写法
function sum(a, b) {
    return a + b;
}

// 调用
sum(1,2); // 3

匿名函数写法

这种方法也称为函数表达式。

// 函数写法
var sum = function (a, b) {
    return a + b;
}

// 调用
sum(2, 3); // 5

函数赋值给一个变量。这是一种声明函数的方式,左边sum是一个变量,右边是一个函数的表达式。意思就是把一个匿名的函数表达式赋值给了变量sum,只是声明了一个变量指向了一个函数对象。

将方法作为一个对象

// 作为对象方法,函数写法

var foo = {
    sum: function(a, b) {
        return a + b
    },
    subtraction: function(a, b) {
        return a - b
    },
    multiplication: function (a, b) {
        return a * b
    }
}

// 调用
foo.sum(1, 4); // 5
foo.subtraction(1, 4); // -3
foo.multiplication(2, 4); // 8

构造函数中给对象添加方法

// 给对象添加方法
var calculate = function() {};
calculate.prototype.sum = function(a, b){
    return a + b
}

在JavaScript中的每个对象都有prototype属性,JavaScript中对象的prototype属性的解释是:返回对象类型原型的引用。比如上面的代码,在calculate的函数上的原始对象添加了一个sum方法,将在构造函数中用到。

// 调用
var calc = new calculate(); //创建对象
calc.sum(1, 2); // 调用对象属性,返回3

自执行函数

JavaScript中自执行函数有几种不同写法:

// 方法一:最前最后加括号
(function(){
    console.log('1'); // 1
}());

// 方法二:function前面加运算符,最常见的是!与void

!function(){
    console.log('1'); // 1
}();

这种自执行函数表达式,主要用于创建一个新的作用域,在此作用域内声明的变量不会和其它作用域内的变量冲突或混淆,大多是以匿名函数方式存在,且立即自动执行。也算是函数表达式。

虽然上面罗列了几种常见创建函数的方法。但在ECMAScript中,创建函数的最常用方法是函数表达式函数声明。初学者对这两者都是比较晕的,比如我自己,很多时候就傻傻的分不清楚什么是函数表达式,什么又是函数声明?为了整清楚,我看书和查资料得知,如果不声明函数名称,它肯定是函数表达式,可如果声明了函数名称的话,如何判断是函数声明还是函数表达式?那又晕呼了:

JavaScript通过上下文秋区分两者,如果function foo() {}是作为赋值表达式的一部分的话,那它就是一个函数表达式;如果function foo() {}被包含在一个函数体内或者位于程序的最顶部的话,那它就是一个函数声明。

// 函数声明
function foo () {} // 它是程序的一部分

(function(){
    function foo() {} // 它是函数体的一部分
})();

// 函数表达式
var bar = function foo() {} // 它是赋值表达式的一部分
new function foo() {} // 它是new表达式
(function foo(){}); // 它包含在分组操作符内

除了上面的之外,还有函数生成器和箭头函数。@Dmitri Pavlutin曾经整理过一篇博文,介绍了在JavaScript中声明函数的六种方法

函数声明的规则

函数声明只能出现在程序或函数体内。从句法上讲,它们 不能出现在Block(块)({ ... })中,例如不能出现在 ifwhilefor 语句中。因为 Block(块) 中只能包含Statement语句, 而不能包含函数声明这样的源元素。另一方面,仔细看一看规则也会发现,唯一可能让表达式出现在Block(块)中情形,就是让它作为表达式语句的一部分。但是,规范明确规定了表达式语句不能以关键字function开头。而这实际上就是说,函数表达式同样也不能出现在Statement语句或Block(块)中(因为Block(块)就是由Statement语句构成的)。

匿名函数

匿名函数是一种在运行时动态声明的函数。它们之所以被称为匿名函数是因为不同于普通函数,它们并没有函数名。 匿名函数是通过函数表达式而不是函数声明语法定义的。你可以在任何可以放置表达式的地方利用函数表达式创建一个新函数。例如你可以定义一个新函数,作为一个函数调用的参数或者作为另一个对象的属性。

从前面的函数声明方式中,或多或少可以知道:

  • 匿名函数是被函数表达式创建的:
  • 匿名函数在运行时被创建
  • 匿名函数不需要函数名

有关于JavaScript中匿名函数的相关介绍,可以阅读众成翻译中的一篇译文:《JavaScript匿名函数》。

箭头函数

通过前面的篇幅,我们对JavaScript中的函数有一些概念性的了解,也算是为接下来的内容打个前哨。我们要了解的是ES6中的箭头函数,这也是JavaScript声明函数的另一种方法。而箭头函数也是ES6中使用率最高的新特性。那什么是箭头函数?

箭头函数在有些地方也称为胖函数,使用箭头=>定义的函数,称为箭头函数,它也属于匿名函数一类。

我们首先了解箭头函数语法,然后我们会发现箭头函数主要优势是:上下文绑定

箭头函数语法

在ES5中我们是这样写函数,比如一个求和的函数:

var sum = function(a, b) {
    return a + b
}

使用箭头=>我们可以分两步实现同样的函数功能。

首先使用=>来替代关键词function

var sum = (a, b) => {
    return a + b
}

如果函数的返回值是一个表达式和函数体没有副作用的话,我们可以省略括号{}return关键词:

var sum = (a, b) => a + b

上面看到的是就是ES6中的箭头函数的使用语法。事实上,ES6的箭头函数有四种使用语法。

单一参数的单行箭头函数

const fn = foo => `${foo} World`

这个箭头函数就只有一个参数foo,并且还省略了大括号{}return关键词。我们可以这样调用:

fn('Hello'); // Hello World

其实上面的函数换成ES5来写就是这样:

var fn = function (foo) {
    return foo + ' World';
}

fn('Hello'); // Hello World

这是箭头函数最简洁的形式,常用于作用简单的处理函数,比如过滤:

// ES5
var array = ['a', 'bc', 'def', 'ghij'];
array = array.filter(function (item) {
    return item.length >= 2;
});

// ES6
let array = ['a', 'bc', 'def', 'ghij'];
array = array.filter(item => item.length >= 2); // "bc", "def", "ghij"

多参数的单行箭头函数

const fn = (foo, bar) => foo + bar
fn(2, 3); // 5

在大多数情况下,函数都不会只有一个参数传入,在箭头函数中,多参数的语法跟普通函数一样,以括号来包裹参数列。上面的函数fn对应的ES5就像下面这样:

// ES5
var fn = function fn(foo, bar) {
    return foo + bar;
};
fn(2, 3); // 5

这种形式常见于数组的处理,比如排序:

// ES5
var array = ['a', 'bc', 'def', 'ghij'];
array = array.sort(function (a, b) {
    return a.length < b.length;
}); // ghij, def, bc, a

// ES6
let array = ['a', 'bc', 'def', 'ghij'];
array = array.sort( (a, b) => a.length < b.length); // ghij, def, bc, a

多行箭头函数

// 单一参数
const fn = foo => {
    return `${foo} World`
}

// 多参数
const fn = (foo, bar) => {
    return foo + bar
}

无参数箭头函数

如果一个箭头函数无参数传入,则需要用一对空的括号来表示空的参数列表。

const fn = () => 'Hello World'

上面的函数fn相当于 ES5的:

var fn = function fn() {
    return 'Hello World';
};

上面几种都是箭头函数表达方式,其最大的好处便是简洁明了。省略了function关键词,而是使用=>代替。相对于传统的function语句,箭头函数在简单的函数使用中更为简洁直观。另外,箭头函数语言简洁的特点使其特别适合用于单行回调函数的定义。

// ES5
var names = ['Will', 'Jack', 'Peter', 'Steve', 'John', 'Hugo', 'Mike'];
var newSet = names.map(function (name, index) {
    return {
        id: index,
        name: name
    };
}).filter(function (man) {
    return man.id % 2 == 0;
}).map(function (man) {
    return [man.name];
}).reduce(function (a, b) {
    return a.concat(b);
});

console.log(newSet); // ["Will", "Peter", "John", "Mike"]

// ES6

const names = ['Will', 'Jack', 'Peter', 'Steve', 'John', 'Hugo', 'Mike'];
const newSet = names
    .map((name, index) => ({
        id: index,
        name: name
    }))
    .filter( man => man.id % 2 == 0)
    .map(man => [man.name])
    .reduce((a, b) => a.concat(b));

console.log(newSet); // ["Will", "Peter", "John", "Mike"]

在ESMAScript这种同时具有函数式编程和面向对象编程特点的语言中,箭头函数可以让代码在编写上变得非常直观和易懂:

  • 将原本的由名字组成的数组转换为一个格式为{id, name}的对象,id则为每个名字在原数组中的位置
  • 剔除其中id为奇数的元素,只保留id为偶数的元素
  • 将剩下的元素转换为一个包含当前元素中原名字的单元数组,以方便下一步的处理
  • 不断合并相邻的两个数组,最后得到的一个数组,便是我们需要得到的目标值

上下文绑定

在ES5中,函数域经常需要将上下文绑定到一个函数。上下文绑定通常表现在以下两个方面:

  • 定义一个变量self = this
  • 使用bind函数

在我们的第一个示例中,我们尝试使用setInterval实现一个球体的动画方法:

var Ball = function( x, y, vx, vy ) {
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
    this.dt = 25; // 1000/25 = 40 frames per second
    setInterval( function() { 
        this.x += vx;  
        this.y += vy;
        console.log( this.x, this.y );
    }, this.dt );
}

var ball = new Ball( 0, 0, 1, 1 );
> NaN NaN  // 25ms later
> NaN NaN  // 50ms later
> NaN NaN  // 75ms later
// ...

动画失效了,是因为setinterval函数参数作用域中的this不对。

为了访问和修改ball对象的变量作用域,我们必须让球在上下文中访问内部的函数参数。我们的第一个解决方案是这样:

var Ball = function( x, y, vx, vy ) {
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
    this.dt = 25; // 1000/25 = 40 frames per second
    var self = this;
    setInterval( function() { 
        self.x += vx;  
        self.y += vy;
        console.log( self.x, self.y );
    }, this.dt );
}

var ball = new Ball( 0, 0, 1, 2 );
> 1 2  // 25ms later
> 2 4  // 50ms later
> 3 6  // 75ms later
// ...

这个解决方案仍然有点尴尬,因为我们必须保证self替代this。在自己的代码中使用self替代this很容易犯错。因此,在ES5中最佳的实践就是建议使用bind方法:

var Ball = function( x, y, vx, vy ) {
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
    this.dt = 25; // 1000/25 = 40 frames per second
    setInterval( function() { 
        this.x += vx;  
        this.y += vy;
        console.log( this.x, this.y );
    }.bind( this ), this.dt );
}

var ball = new Ball( 0, 0, 1, 2 );
> 1 2  // 25ms later
> 2 4  // 50ms later
> 3 6  // 75ms later
// ...

bind方法绑定了setInterval函数中this

在ES6中,箭头函数有自动绑定上下文的特性。箭头函数作用域不会受this影响。因此,您节省思考上下文绑定的时间。

下面的代码是使用ES6重写上面的示例:

var Ball = function( x, y, vx, vy ) {
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
    this.dt = 25; // 1000/25 = 40 frames per second
    setInterval( () => { 
        this.x += vx;  
        this.y += vy;
        console.log( this.x, this.y );
    }, this.dt );
}

b = new Ball( 0, 0, 1, 1 );
> 1 2  // 25ms later
> 2 4  // 50ms later
> 3 6  // 75ms later
// ...

上面的示例演示的就是函数中this的穿透现象。简单点说,箭头函数不绑定this,除了不绑定this之外,还不会绑定argumentssupernew.target。在箭头函数出现之前,每个新定义的函数都有其自己的this值,比如,构造函数的this指向了一个新的对象;严格模式下的函数的this值为undefined;如果函数是作为对象的方法被调用,则其this指向了那个调用它的对象。在面向对象风格的编程中,将会带来很多困扰。

function Person() {
    // 构造函数 Person() 定义的 `this` 就是新实例对象自己
    this.age = 0;
    setInterval(function growUp() {
        // 在非严格模式下,growUp() 函数定义了其内部的 `this`为全局对象, 
        // 不同于构造函数Person()的定义的 `this`
        this.age++;
    }, 3000);
}

var p = new Person();

在ECMAScript3/5中,这个问题通过把this的值赋给变量,然后将该变量放到闭包中来解决。

function Person() {
    var self = this; 
    // 也有人选择使用 `that` 而非 `self`, 只要保证一致就好.
    self.age = 0;
    setInterval(function growUp() {
        // 回调里面的 `self` 变量就指向了期望的那个对象了
        self.age++;
    }, 3000);
}

var p = new Person();

除此之外,还可以使用bind函数,把期望的this值传递给grouUp()函数。

箭头函数会捕获其所在上下文的this值,作为自己的this值,因此下面的代码将如期运行。

function Person() {  
    this.age = 0;  
    setInterval(() => {
        // 回调里面的 `this` 变量就指向了期望的那个对象了
        this.age++;
    }, 3000);
}

var p = new Person();

也就是说:箭头函数会捕获其所在上下文的this作为自己的this,也就是说,箭头函数内部与其外部的this是保持一致的。咱们通过一段ES5和ES6的代码对比,会更易理解:

// ES6
var foo = function(){
    var subFoo = ()=>{
        this.parms = 1;
    }
}

// ES5
var foo = function foo() {
    var _this = this;

    var subFoo = function subFoo() {
        _this.parms = 1;
    };
};

考虑到this是词法层面上的,严格模式中与this相关的规则都将被忽略。

var f = () => {
    'use strict';
    return this
};
f() === window; // 或全局对象

除此之外,由于this已经在词法层面完成了绑定,通过call()apply()方法调用一个函数时,只是传入了参数而已,对this并没有什么影响:

var adder = {
base : 1,

add : function(a) {
    var f = v => v + this.base;
    return f(a);
},

addThruCall: function(a) {
    var f = v => v + this.base;
    var b = {
    base : 2
    };

    return f.call(b, a);
}
};

console.log(adder.add(1));         // 输出 2
console.log(adder.addThruCall(1)); // 仍然输出 2

不绑定参数

箭头函数不会在其内部暴露出参数:arguments.lengtharguments[0]argumetns[1]等等,都不会指向箭头函数的arguments,而是指向了箭头函数所在作用域的一个名为arguments的值,如果没有这个值的话,就是undefined

var arguments = 42;
var arr = () => arguments;

arr(); // 42

function foo() {
    var f = (i) => arguments[0]+i; 
    // foo函数的间接参数绑定
    return f(2);
}

foo(1); // 3

箭头函数没有自己的arguments,不过在大多数情形下,rest参数可以给出一个解决方案:

function foo() { 
    var f = (...args) => args[0]; 
    return f(2); 
}

foo(1); 
// 2

什么时候"不要"用箭头函数

在JavaScript中,方法作为一个函数存储为对象的一个属性。当调用方法时,this指向该方法的从属对象。

对象字面量

自从箭头函数有了短语法,非常吸引人用它来定义方法:

// ES6

var calculate = {  
    array: [1, 2, 3],
    sum: () => {
        console.log(this === window); // => true
        return this.array.reduce((result, item) => result + item);
    }
};
console.log(this === window); // => true  
// Throws "TypeError: Cannot read property 'reduce' of undefined"
calculate.sum();


// ES5
var calculate = {
    array: [1, 2, 3],
    sum: function sum() {
        console.log(undefined === window); // => true
        return undefined.array.reduce(function (result, item) {
        return result + item;
        });
    }
};
console.log(undefined === window); // => true  
// Throws "TypeError: Cannot read property 'reduce' of undefined"
calculate.sum();

calculate.sum 方法是通过箭头函数定义的。但是在调用 calculate.sum() 的时候抛出了一个 TypeError,因为 this.array 的值是 undefined

当调用 calculate 对象上的方法 sum() 时,上下文仍然是 window。这个情况发生是因为箭头函数绑定了该window 对象的词法上下文。

执行 this.array 等同于 window.array, 值为 undefined

解决方案是使用函数表达式或者方法定义的短语法 (ECMAScript 6可用)。 在这种情况下 this 决定于调用的对象,而不是紧邻上下文。 让我们看看修正的版本:

// ES6
var calculate = {  
    array: [1, 2, 3],
    sum() {
        console.log(this === calculate); // => true
        return this.array.reduce((result, item) => result + item);
    }
};
calculate.sum(); // => 6

// ES5
var calculate = {
    array: [1, 2, 3],
    sum: function sum() {
        console.log(this === calculate); // => true
        return this.array.reduce(function (result, item) {
        return result + item;
        });
    }
};
calculate.sum(); // => 6

因为 sum 是一个普通函数,调用 calculate.sum()thiscalculate 对象。 this.array 是数组的引用,因此元素之和计算正确,结果是: 6

对象原型

相同的规则也适用于在 prototype 对象上定义方法。

用箭头函数来定义 sayCatName 方法,会带来一个不正确的上下文 window

// ES6
function MyCat(name) {  
    this.catName = name;
}
MyCat.prototype.sayCatName = () => {  
    console.log(this === window); // => true
    return this.catName;
};
var cat = new MyCat('Mew');  
cat.sayCatName(); // => undefined

//ES5
function MyCat(name) {
    this.catName = name;
}
MyCat.prototype.sayCatName = function () {
    console.log(undefined === window); // => true
    return undefined.catName;
};
var cat = new MyCat('Mew');
cat.sayCatName(); // => undefined

使用 保守派 函数表达式:

// ES6
function MyCat(name) {  
    this.catName = name;
}
MyCat.prototype.sayCatName = function() {  
    console.log(this === cat); // => true
    return this.catName;
};
var cat = new MyCat('Mew');  
cat.sayCatName(); // => 'Mew'

// ES5
function MyCat(name) {
    this.catName = name;
}
MyCat.prototype.sayCatName = function () {
    console.log(this === cat); // => true
    return this.catName;
};
var cat = new MyCat('Mew');
cat.sayCatName(); // => 'Mew'

sayCatName 普通函数被当做方法调用的时候 cat.sayCatName(),会把上下文改变为 cat 对象。

结合动态上下文的回调函数

this 在 JavaScript 中是一个很强大的特性。它允许利用函数调用的方式改变上下文。通常来说,上下文是一个调用发生时候的目标对象,让代码更加 自然化。这就好像 “某些事情正发生在该对象上”。

无论如何,箭头函数在声明的时候都会绑定静态的上下文,而不会是动态的。这是词素 this 不是很必要的一种情况。

给 DOM 元素装配事件监听器是客户端编程的一个通常的任务。一个事件用 this 作为目标元素去触发处理函数。这是一个动态上下文的简便用法。

接下来的例子试图使用一个箭头函数触发一个处理函数:

// ES6
var button = document.getElementById('myButton');  
button.addEventListener('click', () => {  
    console.log(this === window); // => true
    this.innerHTML = 'Clicked button';
});

// ES5
var button = document.getElementById('myButton');
button.addEventListener('click', function () {
    console.log(undefined === window); // => true
    undefined.innerHTML = 'Clicked button';
});

this 在箭头函数中是 window,也就是被定义为全局上下文。当一个点击事件发生的时候,浏览器试着用 button 上下文去调用处理函数,但是箭头函数病不会改变它已经预定义的上下文。

this.innerHTML 等价于 window.innerHTML ,并没有什么意义。

你不得不应用一个函数表达式,去允许目标元素改变其上下文。

// ES6 
var button = document.getElementById('myButton');  
button.addEventListener('click', function() {  
    console.log(this === button); // => true
    this.innerHTML = 'Clicked button';
});

// ES5
var button = document.getElementById('myButton');
button.addEventListener('click', function () {
    console.log(this === button); // => true
    this.innerHTML = 'Clicked button';
});

当用户点击该按钮,this 在处理函数中是 button。 从而 this.innerHTML = 'Clicked button' 正确地修改了按钮的文本去反映点击状态。

调用构造器

this 在一个构造调用过程中是一个新创建的对象。 当执行 new MyFunction(),该构造器的上下文 MyFunction 是一个新的对象: this instanceof MyFunction === true

注意一个箭头函数不能作为构造器。 JavaScript 会通过抛出异常的方式进行隐式地预防。

无论怎样,this 还是会从紧邻上下文中获取,而不是那个新创建的对象。 换句话说,一个箭头函数构造器的调用过程没有什么意义,反而会产生歧义。

让我们看看,如果试图去尝试使用箭头函数作为构造器,会发生什么:

// ES6
var Message = (text) => {  
    this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');

// ES5
var Message = function Message(text) {
    undefined.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');

执行 new Message('Hello World!')Message 是一个箭头函数, JavaScript 抛出了一个 TypeError ,这意味着 Message 不能被用作于构造器。

与一些之前特定版本的 JavaScript 静默失败 相比,我认为 ECMAScript 6 在这些情况下提供含有错误信息的失败会更加高效。

上面的例子可以使用一个 函数表达式 来修正,这才是创建构造器正确的方式 (包括 函数声明):

// ES6
var Message = function(text) {  
    this.text = text;
};
var helloMessage = new Message('Hello World!');  
console.log(helloMessage.text); // => 'Hello World!'

最短语法

箭头函数有一个非常棒的属性,如果函数体只有一条语句的话,可以省略参数的括号 () ,代码块的花括号 {} 以及 return。这对写特别短的函数很有帮助。

我大学的编程教授布置给学生一个有趣的任务:用C语言写一个最短的函数去统计字符串长度。这是一个学习和探索新语言非常好的途径。

然而在真实世界中应用程序的代码是会被许多其他的开发者进行阅读的。最短语法不太适合去帮助你的同事快速理解函数的意义。

在某些程度上来说,压缩的函数会变得阅读困难,所以最好别走入愤怒的深渊。让我们来看一个例子:

// ES6
let multiply = (a, b) => b === undefined ? b => a * b : a * b;  
let double = multiply(2);  
double(3);      // => 6  
multiply(2, 3); // => 6

// ES5
var multiply = function multiply(a, b) {
    return b === undefined ? function (b) {
        return a * b;
    } : a * b;
};
var double = multiply(2);
double(3); // => 6  
multiply(2, 3); // => 6

multiply 返回了两个数字的乘积结果,或者说是一个为了接下来的乘法运算,而关联了第一个参数的闭包。

这个函数运行的很好并且看上去很短。但是它可能第一眼看上去有点难以理解。

为了让它更具有可读性,可以通过给箭头函数添加一些可选的花括号,以及 return 语句,或者是干脆用一个普通函数:

function multiply(a, b) {  
    if (b === undefined) {
        return function(b) {
            return a * b;
        }
    }
    return a * b;
}
let double = multiply(2);  
double(3);      // => 6  
multiply(2, 3); // => 6

最好可以在短和冗长之间寻找到一个平衡点,让你的 JavaScript 更加直接。

特别声明:什么时候“不要”用箭头函数这部分来源于《什么时候“不要”用箭头函数》一文。

什么情况下该使用箭头函数

到这里,我们可以发现箭头函数并不是万金油,稍不留神就会踩坑。

至于什么情况该使用箭头函数,《You Don’t Know About JS》给出了一个决策图:

以上决策图看起来有点复杂,我认为有三点比较重要:

  • 箭头函数适合于无复杂逻辑或者无副作用的纯函数场景下,例如用在mapreducefilter的回调函数定义中;
  • 不要在最外层定义箭头函数,因为在函数内部操作this会很容易污染全局作用域。最起码在箭头函数外部包一层普通函数,将this控制在可见的范围内;
  • 如开头所述,箭头函数最吸引人的地方是简洁。在有多层函数嵌套的情况下,箭头函数的简洁性并没有很大的提升,反而影响了函数的作用范围的识别度,这种情况不建议使用箭头函数。

特别声明:这部分内容来源于《少年,不要滥用箭头函数啊》一文。

使用箭头函数注意事项

箭头函数的typeof运算符和普通的function一样:

var func = a => a
console.log(typeof func); // "function"

instanceof也返回true,表明也是Function的实例:

console.log(func instanceof Function); // true

this固定,不再善变:

obj = {
    data: ['John Backus', 'John Hopcroft'],
    init: function() {
        document.onclick = ev => {
            alert(this.data) // ['John Backus', 'John Hopcroft']
        }
        // 非箭头函数
        // document.onclick = function(ev) {
        //     alert(this.data) // undefined
        // }
    }
}
obj.init()

这个很有用,再不用写meself_thisthat之类了,或者bind

箭头函数不能用new

var Person = (name, age) => {
    this.name = name
    this.age = age
}
var p = new Func('John', 33) // error

另外前面也提到了,箭头函数不能使用argument

ar func = () => {
    console.log(arguments)
}
func(55) 

除此之外,还有一些书写上的小细节需要注意:

没有参数要有小括号

const fn = () => {}

只有一个参数则可以省略小括号

const go = url => {
    location.href = url
}

多个参数

const add = (x, y) => {
    return x + y
}

事件

document.addEventListener('click', e => {
    console.log(e)
})

在ES6中提供了多行箭头函数语法,所以在使用单行箭头函数时,不要对单行的函数体做任何换行,以免出现语法错误。

const fn = x
=> x * 2 // SyntaxError

参数列表的右括号、箭头需要保持在同一行内。

const fn = (x, y) //SyntaxError
=> {
    return x * y
}

正确的写法应该像这样:

const fn = (x, y) => {
    return x * y
}

单行箭头函数只能包含一条语句。但如果是错误抛出语句(throw)等非表达式的语句,则需要使用花括号包裹。

const fn = x => x * 2 // ok
const fn = x => x * 2; return x + 2; // SyntaxError

若要使用单行箭头函数直接返回一个对象字面量,请使用一个括号包裹该对象字面量,而不是直接使用大括号,否则解析引擎会将其解析为一个多行箭头函数。

const ids = [1, 2, 3];
const users = ids.map(id => {id: id})
// => Wrong: [underfined, underfined, undefined]

const users = ids.map(id => ({id:id}))
// =>Correct: [{id: 1}, {id: 2}, {id: 3}]

总结

间间断断看了一些关于ES6中有关于箭头函数的内容,上面整理的内容可能比较零乱,但也可以说涉及到箭头函数相关的内容较多。由于是初学者,里面有不对之处,还请在评论中指正。

扩展阅读

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.w3cplus.com/javascript/es6-arrow-function.html

返回顶部