“箭头函数”是 ECMAScript6 中很是重要的性特性。不少文章都在描述它的上下文透明性以及短语法。新特性必然会带来不少好处,但凡事都有两面性。javascript
本篇文章会经过情景引导,让你知晓哪些情景下应该绕过箭头函数,哪些情景下使用短语法让代码更加精炼。java
在 JavaScript 中,方法做为一个函数存储为对象的一个属性。当调用方法时,this 指向该方法的从属对象。编程
箭头函数简短的书写风格很是吸引人用它来定义方法。让咱们试试看:数组
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 决定于调用的对象,而不是紧邻上下文。 让咱们看看修正的版本:app
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.函数
相同的规则也适用于在 prototype 对象上定义方法。this
用箭头函数来定义 sayCatName 方法,会带来一个不正确的上下文 window:prototype
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 对象。
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 ,并无什么意义。
你不得不该用一个函数表达式,去容许目标元素改变其上下文。
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 仍是会从紧邻上下文中获取,而不是那个新建立的对象。 换句话说,一个箭头函数构造器的调用过程没有什么意义,反而会产生歧义。
让咱们看看,若是试图去尝试使用箭头函数做为构造器,会发生什么:
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 在这些状况下提供含有错误信息的失败会更加高效。
上面的例子可使用一个函数表达式来修正,这才是建立构造器正确的方式 (包括函数声明):
var Message = function(text) { this.text = text; }; var helloMessage = new Message('Hello World!'); console.log(helloMessage.text); // => 'Hello World!'
箭头函数有一个很是棒的属性,若是函数体只有一条语句的话,能够省略参数的括号 () ,代码块的花括号 {} 以及 return (译者注:此处省略参数的括号,与函数体只有一条语句不要紧)。这对写特别短的函数颇有帮助。
单个入参时可省略()
,箭头函数代码块部分只有单条语句返回时,可省略{}
和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; }
在日常的工做中应用程序的代码是会被许多其余的开发者进行阅读的。最短语法不太适合去帮助你的同事快速理解函数的意义。在某些程度上来讲,压缩的函数会变得阅读困难,因此最好别走入愤怒的深渊。让咱们来看一个例子:
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 更加直接。
箭头函数能够很方便的帮助开发者指定 this 的指向,一样咱们还有不少种其余的方式来完成 this 做用域的绑定。好比:
在某些状况下,优点也会带来劣势。当要求动态上下文的时候,你就不能使用箭头函数,好比:定义方法,用构造器建立对象,处理时间时用 this 获取目标。
毫无疑问,箭头函数是一个很是好的特性加强。使用正确的话,它会在不少地方带来方便,好比早期的时候,你不得不使用 .bind() 或者 试图去捕获上下文。固然,它也让代码变得更加轻便。