作者:扉扉 (沪江web前端开发工程师)
本文原创翻译,有不当的地方欢迎指出。转载请指明出处。
看到你每天使用的编程语言在不断进化是一件令人开心的事情 。从错误中学习,找到更好的实现方式,创造新的语法特性,语言就这样一步一步地实现了版本更新。
这正是近几年Javascript身上发生的事情 ,ECMAScript6 引入了: 箭头函数,类以及其它特性,真的太棒了!
其中一个非常好用的箭头函数,有许多文件介绍了了这个漂亮的语法糖,还具有透明上下文的作用(原文为 context transparency), 如果你对于ES6还不熟悉,请先阅读箭头函数的一些入门文章。
凡事都有两面性,新的特性往往也会带来新的困扰, 比如对箭头函数的误用。
这篇文章通过实际使用场景带你了解在一些特定情况下到底是应该使用传统的函数,还是该使用更简洁的箭头函数。
1.在对象上定义方法
在javascript中,方法可以做为一个对象的属性,当调用这个方法时, this 指向这个方法所属的对象;
1a. 对象字面量
既然箭头函数只是一个语法糖,那我们来尝试一下使用箭头函数做为一个对象的方法会发生什么:
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();
calculate.sum使用箭头函数来定义,但是调用 calculate.sum() 时出现了异常。因为当执行sum的时候上下文仍然是window,这是因为箭头函数已经绑定了window做为上下文。
执行this.array 等同于 window.array ,当然是 undefined
解决办法就是不要在对象的方法上使用箭头函数短语法,这样this关键字会在调用时决定,而不是早早绑定在闭合的上下文中, 让我们看一下具体代码:
var calculate = {
array: [1, 2, 3],
sum() {
console.log(this === calculate); // => true
return this.array.reduce((result, item) => result + item);
}
};
calculate.sum(); // => 6
对象原型
同样的规则也适用于给对象prototype原型上定义方法:
function MyCat(name) {
this.catName = name;
}
MyCat.prototype.sayCatName = () => {
console.log(this === window); // => true
returnthis.catName;
};
var cat = new MyCat('Mew');
cat.sayCatName(); // => undefined
使用传统方式即可正常工作:
function MyCat(name) {
this.catName = name;
}
MyCat.prototype.sayCatName = function() {
console.log(this === cat); // => true
returnthis.catName;
};
var cat = new MyCat('Mew');
cat.sayCatName(); // => 'Mew'
2.动态上下文中的回调函数
this是js中非常强大的特点,他让函数可以根据其调用方式动态的改变上下文,然后箭头函数直接在声明时就绑定了this对象,所以不再是动态的。
在客户端,在dom元素上绑定事件监听函数是非常普遍的行为,在dom事件被触发时,回调函数中的this指向该dom,可当我们使用箭头函数时:
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
因为这个回调的箭头函数是在全局上下文中被定义的,所以他的this是window。所以当this是由目标对象决定时,我们应该使用函数表达式:
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
3.调用构造函数
当函数做为构造函数执行时 new MyFunction(),this指向新创建的对象实例:
this instanceOf MyFunction === true
需要注意的是,构造函数不能使用箭头函数,如果这样做会抛出异常。
因为使用箭头函数后this会指定闭合的当前上下文,而当函数做为构造器的时候,this又会指向生成的实例, 这个造成歧义。
var Message = (text) => {
this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');
我们都知道使用函数表达式即可正常:
var Message = function(text) {
this.text = text;
};
var helloMessage = new Message('Hello World!');
console.log(helloMessage.text); // => 'Hello World!'
4.超短的语法
箭头函数可以让语句写的非常的简洁,参数只有一个时可以省略(),函数体只有一句话可以省略{},如果返回值是一个表达式还甚至还可以省略return!
我的大学老师曾给我们布置了一道有趣的作业: 使用C语言来编写一个计算字符串长度的函数,函数要尽可能的短,这是一个很好的方法去学习一门新的语言。
不过在真实生活中,代码要被其它开发者阅读,超短的语法有时会让你的同事陷入难以理解中。上代码:
let double = multiply(2);
double(3); // => 6
multiply(2, 3); // =>6
这个函数的作用就是当只有一个参数 a 时,返回接受一个参数 b 返回 a * b 的函数,接收两个参数时直接返回乘积,这个函数可以很好的工作并且看起很简洁,但是从第一眼看去并不是很好理解。
为了让这个函数更好的让人理解,我们可以为这个箭头函数加一对花括号,并加上 return 语句,或者直接使用函数表达式:
function multiply(a, b){
if (b === undefined) {
return function(b){
return a * b;
}
}
return a * b;
}
letdouble = multiply(2);
double(3); // => 6
multiply(2, 3);// => 6
怎么样是不是好理解多了?
如何平衡简洁与易理解也是使用箭头函数需要注意的地方。
5.总结
毫无疑问,箭头函数是一个很棒的特性。以前我们使用bind()函数或者需要固定上下文的地方现在使用箭头函数会让代码更加简洁。
但有一些情况下,使用箭头函数也有一些不便利。在需要动态上下文的地方不能使用箭头函数,使用构造函数创建对象时不能使用箭头函数等等。除去文中列举不适合使用的情况下,尽情地使用箭头函数吧。
iKcamp原创新书《移动Web前端高效开发实战》已在亚马逊、京东、当当开售。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。