UDN-企业互联网技术人气社区

板块导航

浏览  : 533
回复  : 2

[原生js] 什么时候不使用箭头函数

[复制链接]
htmlman的头像 楼主
发表于 2017-1-2 16:05:37 | 显示全部楼层 |阅读模式
  看到代码每天都在进步是一件快乐的事情,在错误中学习,搜索更好的实现,创造新的特性,这些行为让代码在版本迭代中越来越好。

  这就是JavaScript这几年发生的事,ES6带来了很多新特性:箭头函数、类等等。这些都是一些伟大的变化,特别是箭头函数。已经有很多的文章对箭头函数的特性进行了介绍,如果你刚刚接触ES6,我建议你去看 这篇文章

  然而事情都有两面性,新特性某些时候也会带来混乱,其中之一就是乱用箭头函数的问题。

  这篇文章举了一些示例来说明什么地方你应该放弃使用箭头函数而去使用旧的函数声明表达式或者短方法语法(shorthand method syntax)。并且给出简短的解决方法,这可以让代码的可读性更高。

  1.定义对象方法时(Defining methods on an object)

  JavaScript中的方法就是一个作为对象属性的函数,当一个方法被调用时, this 就指向了方法所属的对象。

  1a.对象方法(Object literal)

  因为箭头函数的语法简短,使用它来定义函数方法是很吸引人的。让我们试一下:
  1. var calculate = {  
  2.   array: [1, 2, 3],
  3.   sum: () => {
  4.     console.log(this === window); // => true
  5.     return this.array.reduce((result, item) => result + item);
  6.   }
  7. };
  8. console.log(this === window); // => true
  9. // Throws "TypeError: Cannot read property 'reduce' of undefined"
  10. calculate.sum();
复制代码

  我们使用了箭头函数去定义 calculate.sum 方法,但调用 calculate.sum() 却引起了一个 TypeError 异常,因为 this.array 被设置成了 undefined 。

  当我们在 calculate 对象上调用 sum() 方法时,上下文仍然是 window ,这是因为箭头函数的词法作用域绑定在了 window 对象上。(注:关于箭头函数的this可以参考 廖大的网站 ,讲的很详细。)

  所以调用 this.arrow 等同于调用 window.arrow ,即 undefined 。

  解决方法就是使用函数表达式或者 短语法 (ES6新特性)来定义方法,这种情况下 this 将由调用者决定,而不是闭包中。下面是修改后的版本:
  1. var calculate = {  
  2.   array: [1, 2, 3],
  3.   sum() {
  4.     console.log(this === calculate); // => true
  5.     return this.array.reduce((result, item) => result + item);
  6.   }
  7. };
  8. calculate.sum(); // => 6
复制代码

  1b.对象原型(Object prototype)

  同样的规则也适用于 prototype 对象上,使用箭头函数来定义 sayCatName 方法,造成了错误的上下文环境为 window :
  1. functionMyCat(name){  
  2.   this.catName = name;
  3. }
  4. MyCat.prototype.sayCatName = () => {  
  5.   console.log(this === window); // => true
  6.   return this.catName;
  7. };
  8. var cat = new MyCat('Mew');  
  9. cat.sayCatName(); // => undefined
复制代码

  使用老的函数表达式来解决:
  1. functionMyCat(name){  
  2.   this.catName = name;
  3. }
  4. MyCat.prototype.sayCatName = function(){  
  5.   console.log(this === cat); // => true
  6.   return this.catName;
  7. };
  8. var cat = new MyCat('Mew');  
  9. cat.sayCatName(); // => 'Mew'
复制代码

  当我们使用 cat.sayCatName() 这种方式调用时,我们将上下文环境绑定到了 cat 对象上。

  2.回调函数中(Callback functions with dynamic context)

  JavaScript中的 this 是一个很给力的特性,它准许在函数调用时改变上下文。调用目标对象时频繁的切换上下文,可以使代码看起来更加“自然”,就好象说“这个对象发生什么事情”一样。

  然而箭头函数在声明时候静态的绑定了上下文环境,并且不可动态修改。这样的好处就是不用考虑 this 的词法作用域了。

  很常见的一种情况就是给DOM元素添加监听事件。目标元素作为 this 触发了事件监听函数,这样可以简单的使用动态上下文环境了。

  下面的例子尝试使用箭头函数作为回调函数:
  1. var button = document.getElementById('myButton');  
  2. button.addEventListener('click', () => {  
  3.   console.log(this === window); // => true
  4.   this.innerHTML = 'Clicked button';
  5. });
复制代码

  在全局上下文中 this 被指向了 window ,当点击时间发生时,浏览器尝试在 button 的上下文中去调用回调函数,但箭头函数并没有改变之前定义的上下文。 this.innerHTML 等同于 window.innerHTML 并且没任何意义。

  这时你不得不使用函数表达式,准许 this 根据目标元素来改变。
  1. var button = document.getElementById('myButton');  
  2. button.addEventListener('click', function(){  
  3.   console.log(this === button); // => true
  4.   this.innerHTML = 'Clicked button';
  5. });
复制代码

  当用户点击按钮后,在回调函数中 this 指向了 button ,因此点击之后 this.innerHTML = 'Clicked button' 正确的修改了按钮文本。

  3.构造函数中(Invoking constructors)

  在构造函数中使用 this 来创建新对象,当执行 new MyFunction() 时,构造函数 MyFunction 的上下文是一个新的对象: this instanceof MyFunction === true 。

  要注意,箭头函数并不能作为构造函数使用。JavaScript通过引发异常来隐式阻止那么做。不管怎么样, this 在闭包的上下文中已经被设定而不是一个新创建的对象。换句话说,箭头函数作为构造函数使用并不是一种明确的做法并且会引起歧义。

  来看看下面的代码会发生什么:
  1. var Message = (text) => {  
  2.   this.text = text;
  3. };
  4. // Throws "TypeError: Message is not a constructor"
  5. var helloMessage = new Message('Hello World!');
复制代码

  执行 new Message('Hello World!') , Message 是一个箭头函数,JavaScript引发了一个 TypeError 来阻止 Message 作为构造函数来使用。

  我认为ES6中明确的给出了详细的错误提示是非常好的,相反在之前的版本中会静默的失败而没有任何提示。

  下面的例子使用函数表达式来正确的创建构造函数:
  1. var Message = function(text){  
  2.   this.text = text;
  3. };
  4. var helloMessage = new Message('Hello World!');  
  5. console.log(helloMessage.text); // => 'Hello World!'
复制代码

  4.语句过短时(Too short syntax)

  箭头函数可以省略括号、花括号甚至在函数体只有一句话时候连return都可以省略,这有助于写出简短的代码。

  我的大学教授给学生一个很有意思的任务:使用C语言编写尽可能短的函数,这是一个好方法去学习、探索一个新的语言。

  然而在真实世界中程序的代码会被很多开发者阅读。过于简洁的代码并不有助于你的同事正确理解函数的作用。

  过于抽象的函数很难被理解,所以别手里有个锤子看什么都是钉子。让我们看下面这个栗子:
  1. let multiply = (a, b) => b === undefined ? b => a * b : a * b;  
  2. let double = multiply(2);  
  3. double(3);      // => 6
  4. multiply(2, 3); // => 6
复制代码

  multiply 返回两个数的乘积或者使用第一个参数为后续的运算创建一个闭包。

  这个函数可以正常使用并且十分简短,但是第一眼看上去太难理解了。

  为了可读性,我们将这个箭头函数还原回函数表达式:
  1. functionmultiply(a, b){  
  2.   if (b === undefined) {
  3.     return function(b){
  4.       return a * b;
  5.     }
  6.   }
  7.   return a * b;
  8. }
  9. let double = multiply(2);  
  10. double(3);      // => 6
  11. multiply(2, 3); // => 6
复制代码

  在简短和冗余间做出权衡让你的代码更加清晰明确。

  5.结论(Conclusion)

  毫无疑问箭头函数是一个不错的特性,正确的使用可以让代码更加简单明了,特别是早些时候不得不使用 .bind() 或者捕获上下文的时候。

  事物都有两面性,你不能在需要动态上下文环境的情况下使用箭头函数:定义方法时,使用构造函数创建对象时,当处理事件时需要使用 this 从目标获取上下文的情况。

相关帖子

发表于 2017-1-2 16:06:08 来自手机 | 显示全部楼层
LZ是闲人,天天发帖,坚定完毕
使用道具 举报

回复

发表于 2017-1-6 03:34:33 来自手机 | 显示全部楼层
纯粹路过,没任何兴趣,仅仅是看在老会员的份上回复一下
使用道具 举报

回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部