Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

ES6 箭式函数(Arrow Function)的设计逻辑

  原先我很纠结ES6的箭式函数(Arrow Function)中this的设定,因为this被强制绑定到了定义箭式函数所在作用域的this上,并且不受callapply的影响,这种设定很奇怪吧?但在和@IntPtr同学讨论过后发现,确实是我对箭式函数的概念理解错了。
  一开始我只是觉得箭式函数是一种函数定义的缩写,但这个理解好像有点问题。如果仅是这样的概念,this就不应该被做如此生硬的处理。比如下面这个代码应该得到什么结果?
<script>
(function(){ (()=>console.log(this)).call([2]); }).call([1]);
</script>
  这个代码执行得到的是[1]。其实,箭式函数应该被理解为一种介于普通函数和块作用域之间的东西。比起块作用域,它可以接收参数,也可以返回结果。它自身还作为一个表达式,因此可以作为回调被传递,而且还继承于Function。但它比起函数来也是有欠缺的!它没有自己的this,访问this的话得到的是定义所在作用域的this,即使使用callapply也无法将this传入。因此,它也不能作为构造器,不能使用new对其实例化。
  至此都是比较容易理解的。但是箭式函数为什么被如此设计呢?只是为了让this便于使用吗?确实,这可以避免一些var that=this;之类的东西出现,但实际上如果就此讨论的话我觉得还是弊大于利的。因为对回调函数传入this是非常普遍的做法。比如jQuery的回调就基本都有定义this//当事件触发时,this是指向BODY这个DOM元素的
$("body").click(function(){ console.log(this); });
  或者AMD定义模块的话,factory函数也有传入thisdefine("test",function(require,exports,module){
  console.log(exports==this);
});
  除了这两个典型的之外,还有很多很多对回调函数传入this的封装,这里就不多举例了。回到正题上,既然这么设计并不便于使用,为什么还要这么设计呢?因为存在概念上的问题呐!比如有这样的写法 new function(){
  //这个this看起来是不是很像当前作用域中的this呢?
  //   ↓
  ()=>this;
  //   ↑
  //至少第一眼看着像,所以很容易被当做当前作用域this来使用。
  //于是它就直接被设计成当前作用域的this了。
  //而且无论箭式函数在使用时被call还是被apply都不会影响。
  //如同箭式函数本身没有this一样。
};
  我觉得,这才是重点。因为箭式函数的定义不存在function关键字,可能不会被认为是一个函数作用域。于是在对this的处理上使用了块作用域的处理方式。即便如此,还是有点诡异的感觉,比如下面这种写法:
(function(a,b,c){
  //这个arguments看起来是不是很像当前作用域中的arguments呢?
  //但实际上它并不是,它确实是箭式函数中的arguments!
  //     ↓
  ()=>arguments;
})(1,2,3);
  arguments并没有采取与this相同的处理。箭式函数可以有形参列表,可以接收参数,所以arguments确实是箭式函数自己的东西。实际上thisarguments本身就是完全不同的概念。this是一个主值表达式,属于运算符,而arguments是一个环境记录项,属于变量。就像我们往箭式函数中传入变量一样,虽然变量也看似外层作用域的,但箭式函数确实有对变量的正常处理能力,所以这样的处理是可以被理解的。
  以上就是ES6中箭式函数的设计逻辑。逻辑上本身是没问题的,但对于把this指向箭式函数定义所在的作用域的this这件事,我有其它看法。这么定义确实比默认指向global要方便,对此我是没有异议的。但callapply都无法对其传入this的话,在回调使用this的场景就无法使用箭式函数了。是不是可以考虑让函数有个默认的this指向,比如普通函数依然默认指向global,而箭式函数默认指向定义所在的作用域this,并且允许callapply传入this来取代默认this指向。
  也许有人会觉得,箭式函数这样不受callapply影响的this很安全,如此处理是保证this不被劫持。我认为,如果开发者不希望自己函数中的this受到其它影响,可以手动执行bind,而不该依赖于箭式函数的处理。实际上以我的开发经验来看,这种“不希望自己函数中的this受到影响”的应用场景远不如回调函数中使用this的场景普遍。如果默认就屏蔽掉所有callapply传入的this,那整个适用范围就缩了一圈。反过来,如果默认不屏蔽这些,而在需要稳定的this时手动调用bind去屏蔽,这样适用范围会更广。
网名:
3.84.186.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^