「箭头函数」是 ECMAScript6 中非常重要的性特性。很多文章都在描述它的上下文透明性以及短语法。新特性必然会带来很多好处,但凡事都有两面性。本篇文章会通过情景引导,让你知晓哪些情景下应该绕过箭头函数,哪些情景下使用短语法让代码更加精炼。

特性介绍

单个入参时可省略(),箭头函数代码块部分只有单条语句返回时,可省略{}return

var f = v => v;
// 等同于
var f = function(v) {
return v;
} var f = () => 5;
// 等同于
var f = function() {
return 5;
} var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
} /* 返回值类型为object,需要使用{}包裹 */
var getTempItem = id => ({id: id, name: 'Temp'});

箭头函数配合变量结构使用。

const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
return person.first + ' ' + person.last;
}

滥用下短语法会影响代码可读性。

  1. 在对象上定义方法

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

  1. 对象字面量

箭头函数简短的书写风格非常吸引人用它来定义方法。让我们试试看:

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

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

当调用 calculate.sum() 方法时,this 不是指向 calculate 实例,而是指向 window 对象。那么执行 this.array 等同于window.array,array 是 calculate 对象的属性,故 window.array 为 undefined。

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

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

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

  1. 对象原型

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

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

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

使用函数表达式:

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 对象。

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

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

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

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

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

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

this 在箭头函数中是 window,也就是被定义为全局上下文(译者注:这里应该描述的就是上文的例子)。当一个点击事件发生的时候,浏览器试着用 button 上下文去调用处理函数,但是箭头函数并不会改变它已经预定义的上下文。

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

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

在 JS Bin 尝试

var button = document.getElementById('myButton'); 
button.addEventListener('click', function() { 
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
当用户点击该按钮,this 在处理函数中是 button。 从而 this.innerHTML = 'Clicked button' 正确地修改了按钮的文本去反映点击状态。

  1. 调用构造器

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

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

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

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

在 JS Bin 尝试

var Message = (text) => { 
this.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 在这些情况下提供含有错误信息的失败会更加高效。

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

在 JS Bin 尝试

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

  1. 最短语法

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

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

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

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

let multiply = (a, b) => b === undefined ? b => a * b : a * b;
let 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 更加直接。

  1. 结论

毫无疑问,箭头函数是一个非常好的特性增强。使用正确的话,它会在很多地方带来方便,比如早期的时候,你不得不使用 .bind() 或者 试图去捕获上下文。当然,它也让代码变得更加轻便。

参考

在某些情况下,优势也会带来劣势。当要求动态上下文的时候,你就不能使用箭头函数,比如:定义方法,用构造器创建对象,处理时间时用 this 获取目标。

es6--之箭头函数的更多相关文章

  1. ES6中箭头函数的作用

    我们知道在ES6中,引入了箭头函数,其本质就是等同有ES5中的函数.类似于下面的写法: let test1=() => “abc”; let test2=() => { return “a ...

  2. Es6中箭头函数与普通函数的区别

    Es6中箭头函数与普通函数的区别? 普通function的声明在变量提升中是最高的,箭头函数没有函数提升 箭头函数没有属于自己的this,arguments 箭头函数不能作为构造函数,不能被new,没 ...

  3. es6的箭头函数和es5的function函数区别

    一.es6的箭头函数es6箭头函数内部没有this,使用时会上朔寻找最近的this不可以做构造函数,不能使用new命令,因为没有this函数体内没有arguments,可以使用rest参数代替不能用y ...

  4. Es6 之箭头函数 初学

    不积跬步,无以至千里;不积小流,无以成江海! // ES5 var selected = allJobs.filter(function (job) { return job.isSelected() ...

  5. 关于es6的箭头函数使用与内部this指向

    特型介绍:箭头函数是ES6新增的特性之一,它为JS这门语言提供了一种全新的书写函数的语法. 'use strcit'; let arr = [1,2,3]; //ES5 let es5 = arr.m ...

  6. ES6笔记② 箭头函数

    特性介绍 箭头函数是ES6新增的特性之一,它为JS这门语言提供了一种全新的书写函数的语法. //ES5 function fun(x,y){ return x+y; } console.log(fun ...

  7. 关于ES6 用箭头函数后的 this 指向问题

    最近写完小程序后, 开始学习React, 因为有编译器, 就直接用ES6 新语法了, 中间自然离不开  () => { console.log('箭头函数的this是指向哪的问题')}; var ...

  8. 进阶路上有你我-相互相持篇之ES6里箭头函数里的this指向问题

    首先复习下普通函数里的this指向: function test(){ console.log(this) } test() 你会秒杀的毫无疑问的回答:window,针对普通函数:谁调用了函数  函数 ...

  9. es6之箭头函数

    => 箭头函数是ES6增加的函数表达式.将function关键字和函数名都删掉,并使用“=>”连接参数列表和函数体(低版本浏览器不支持) 箭头函数看上去只是语法的变动,其实也影响了this ...

  10. ES6学习--箭头函数

    1. 箭头函数基本形式 let func = (num) => num; let func = () => num; let sum = (num1,num2) => num1 + ...

随机推荐

  1. volatile关键字简单摘要

    volatile就可以说是java虚拟机提供的最轻量级的同步机制 特性: 1.保证共享变量的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的——要了解主存.高速缓存还有Java内 ...

  2. 102 Binary Tree Level Order Traversal 二叉树的层次遍历

    给定一个二叉树,返回其按层次遍历的节点值. (即zhu'ceng'de,从左到右访问).例如:给定二叉树: [3,9,20,null,null,15,7],    3   / \  9  20    ...

  3. DiscuzX2.5密码错误次数过多,请 15 分钟后重新登录的修改办法

    source\function function_login.php $return = (!$login || (TIMESTAMP - $login['lastupdate'] > )) ? ...

  4. springBoot + mybatis实现执行多条sql语句出错解决方法

    在Idea中执行多条sql语句的修改(mybatis默认的是执行sql语句是执行单条,所以要执行多条的时候需要进行配置) 需要在连接字符串中添加上&allowMultiQueries=true ...

  5. Spring框架学习——AOP的开发

    一.AOP开发中的相关术语. ——JoinPoint(连接点):指那些可以被拦截到的点.比如增删改查方法都可以增强,这些方法就可以被称为是连接点. ——PointCut:切入点,真正被拦截的点,指对哪 ...

  6. jQuery Deferred对象详细源码分析(-)

    本系列文章讲介绍这个Deferred东西到底拿来干什么,从1.5版本加进来,jQuery的很多代码都重写了.直接先上源码分析了,清楚了源码分析,下节将讲具体的应用 以及应用场景. 创建对象 var d ...

  7. centos 7下Hadoop 2.7.2 伪分布式安装

    centos 7 下Hadoop 2.7.2 伪分布式安装,安装jdk,免密匙登录,配置mapreduce,配置YARN.详细步骤如下: 1.0 安装JDK 1.1 查看是否安装了openjdk [l ...

  8. DataPicker以及TimePicker显示时间和日期(屏幕上显示)

    public class MainActivity extends Activity { private DatePicker date_picker;private TimePicker time_ ...

  9. 【Web应用-迁移】迁移 Web 应用到新的应用服务计划的相关限制和说明

    现象描述 当前 Web 应用所在的应用服务计划和目标应用服务计划属于同一个资源组,但是通过 Portal 点击 “更改应用服务计划”,依旧看不到目标应用服务计划. 问题分析 导致上述问题的原因是,用户 ...

  10. SqlDbx远程链接DB2数据库

    1.首先下载IBM的IBM Data Server Client,百度云链接:http://pan.baidu.com/s/1kVBVjan 密码:2gtz 2.安装好客户端之后,打开cmd,运行db ...