箭头函数=>无疑是ES6中最受关注的一个新特性了,通过它可以简写 function 函数表达式,你也可以在各种提及箭头函数的地方看到这样的观点——“=> 就是一个新的 function”。

箭头函数的句法规则甚至早已延伸到各项标准和技术文档中去了,虽然它早已不稀奇,却给我们一种刚刚发现的新鲜感。 

粉我的人都知道俺因为某些原因不怎么喜欢 => 的语法,不过别担心,本文并非讲述我为何不喜欢它,如果你对这个观点感兴趣,可以查看我《YDKJS:ES6 & Beyonf》一书的第二章

我想在这里理清一下箭头函数到底对 this 和 arguments 等东东做了些啥,事实上我在之前从未准确解释过这一点,对此感到有点愧疚,于是乎想洗白下自己。你可以在这里看到我对于该话题的第一次陈述。

是否局部(Lexical)?

包括我在内的许多人,都会这么描述箭头函数里 this 的行为:局部的 this。

什么意思呢?

function foo() {
setTimeout( () => {
console.log("id:", this.id);
},100);
} foo.call( { id: 42 } );
// id: 42

这里的 => 箭头函数看起来把它内部的 this 绑定为父函数 foo() 里的 this。如果这个内部函数是一个常规的函数(声明或表达式),它的 this 将类似 setTimeout 如何调用函数一样被控制着。如果你对 this 绑定的规则还不清楚,可以查阅我《YDKJS:this & Object Prototypes》一书的第二章

局部变量 this

一个描述 this 行为观察的常用伎俩是:

function foo() {
var self = this;
setTimeout(function() {
console.log("id:", self.id);
},100);
} foo.call( { id: 42 } );
// id: 42

旁注:上方“self”的变量名其实是一个非常糟糕、容易误解的名字,它意味着把 this 指向函数自己,而它并没有这么做。

var that = this 也是一个同样不妥的语义,特别当存在多个作用域而使用(that1, that2, ...)的时候更糟糕。如果你想起个语义妥当的好名字,可以试试 var context = this,因为它能准确描述 this 是什么——一个动态的上下文。

从上方的代码段我们可以看到,我们并没有在内部函数中使用到 this,取而代之的是一个更具预见性的局部变量。我们在外部函数中声明了变量 self,简单地关联了内部函数里用到的变量。

这么一来我们通过使用局部作用域以及闭包的原理,彻底地绕过方程式(示例代码中的内部函数)中绑定 this 的规则。

这样的结果看起来跟 => 箭头函数是一样的,换句话说,我们会(错误地)认为 => 箭头函数有着一个跟局部变量/闭包机制一样的“局部 this”行为。

但这种观点并不正确,坑爹了。

箭头函数的this绑定

咱可通过另一个方法来观察箭头函数中 this 的行为——给内部函数做一个强制绑定:

function foo() {
setTimeout(function() {
console.log("id:", this.id);
}.bind(this),100);
} foo.call( { id: 42 } );
// id: 42

你可以看到我们使用了 .bind(this) 来把内部函数中的 this 绑定到了外部函数去,这样一来无论 setTimeout 会选择如何调用赋予它的函数,该函数都会使用 foo() 里所使用到的 this。

是的,这个版本的代码中我们观测到的行为跟之前两段示例代码所要论述的一样,它更准确么?许多童鞋都认为 => 箭头函数就是这么工作的。

啧啧~图样图森破了~

生来局部

TC39的常客 Dave Herman 曾更仔细、准确地向我阐述过这个问题,但我很愧疚一直没能完全了解他所陈述的含义,因此对于我往日不准确的言论我就更感歉意了,也更能接纳他人的观点。

Dave 主要对我这么说,“你提及的'局部 this'的描述很蹩脚,因为 this 无论如何都是局部的”。

真的么?嗯哼~

他继续说道,“箭头函数 => 所改变的并非把 this 局部化,而是完全不把 this 绑定到里面去”。

等等,这样合理么?我明明可以在 => 箭头函数里使用 this 的不是么?

当然可以,不过一切是这么发生的 —— 虽然 => 箭头函数没有一个自己的 this,但当你在内部使用了 this,常规的局部作用域准则就起作用了,它会指向最近一层作用域内的 this。

来个示例:

function foo() {
return () => {
return () => {
return () => {
console.log("id:", this.id);
};
};
};
} foo.call( { id: 42 } )()()();
// id: 42

思考下,在这段代码中,

有多少次 this 的绑定执行了呢?大部分人会认为有4次——每个函数里各一次。

事实上更准确地说,只有一次才对,它发生于 foo() 函数中。

这些接连内嵌的函数们都没有声明它们自己的 this,所以 this.id 的引用会简单地顺着作用域链查找,一直查到 foo() 函数,它是第一处能找到一个确切存在的 this 的地方。

说白了跟其它局部变量的常规处理是一致的!

换句话说,正如同 Dave 说的一样,this 生来局部,而且一直都保持局部态。=>箭头函数并不会绑定一个 this 变量,它的作用域会如同寻常所做的一样一层层地去往上查找。

不仅仅是this

如果你贸贸然地同意了“箭头函数就是常规function的语法糖”这样的观点,那是不正确的,因为事实并非如此——箭头函数里并不按常规支持 var self = this 或者 .bind(this) 这样的糖果。

那些错误的解释都是典型的“给对了答案却讲错了原因”,就像你在高中代数课的测试上明明写对了答案,但老师仍会画圈圈告诉你用错方法了——如何解得答案才是最重要的!

另外,关于“=>箭头函数不绑定自身的 this,而允许局部作用域的方案来沿袭处理之”的正确描述,也解释了箭头函数的另一个情况——它们在函数内部不走寻常路的孩子不仅仅是 this。

事实上 =>箭头函数并不绑定 this,arguments,super(ES6),抑或 new.target(ES6)。

这是真的,对于上述的四个(未来可能有更多)地方,箭头函数不会绑定那些局部变量,所有涉及它们的引用,都会沿袭向上查找外层作用域链的方案来处理。

思考下这段代码:

function foo() {
setTimeout( () => {
console.log("args:", arguments);
},100);
} foo( 2, 4, 6, 8 );
// args: [2, 4, 6, 8]

这段代码中,=>箭头函数并没有绑定 arguments,所以它会以 foo() 的 arguments 来取而代之,而 super 和 new.target 也是一样的情况。

总结

不要不经思考就轻易接受那些不准确的答案,不用满足于那些通过错误形式获取到的正确答案。

这关系到了事物是怎样作业的,以及你使用了怎样的心智模型(mental model),你会使用这种心智模型去分析、描述和调试其它的行为,如果你在一开始的时候就偏离了轨道,那么在之后你也只会一直停留在错误的轨道上。

我后悔当初没有更仔细地聆听 Dave 的观点,也好希望当初自己木有发表过关于=>箭头函数的错误言论。我会在今后思考、提笔、传道JS的时候更加严格地确保其正确性,也会让自己更加小心谨慎。

原文 http://blog.getify.com/arrow-this/

ES6 箭头函数中的 this?你可能想多了(翻译)的更多相关文章

  1. es6箭头函数中this

    普通函数: $scope.$on('$stateChangeSuccess',function(){this.list = this.getList();}); 箭头函数: $scope.$on('$ ...

  2. 深入理解ES6箭头函数中的this

    简要介绍:箭头函数中的this,指向与一般function定义的函数不同,比较容易绕晕,箭头函数this的定义:箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定. 1.何为定义时 ...

  3. es6箭头函数 this 指向问题

    es5中 this 的指向 var factory = function(){ this.a = 'a'; this.b = 'b'; this.c = { a:'a+', b:function(){ ...

  4. ES6之箭头函数中的this

    在讲箭头函数中的this之前我们先介绍一下普通函数中的this.      普通函数中的this: (1)this指向它的直接调用者 (2)默认的,非严格模式下,没找到直接调用者则指向window ( ...

  5. es6 箭头函数(arrow function) 学习笔记

    箭头函数有两个好处. 1.他们比传统函数表达式简洁. const arr = [1, 2, 3]; const squares = arr.map(x => x * x); // 传统函数表达式 ...

  6. es6箭头函数讲解

    es6箭头函数的用法 箭头函数是es6的一种函数的简写方法. 如下: var f = v = > v; //等同于 var f = function(v){ return v; } var su ...

  7. js中的this和箭头函数中的this

    一.ES6 允许使用"箭头"(=>)定义函数. // var f = v => v;// 上面的箭头函数等同于: // var f = function(v) {// ...

  8. ES6 — 箭头函数

    一 为什么要有箭头函数 我们在日常开发中,可能会需要写类似下面的代码 const Person = { 'name': 'little bear', 'age': 18, 'sayHello': fu ...

  9. 前端分享----JS异步编程+ES6箭头函数

    前端分享----JS异步编程+ES6箭头函数 ##概述Javascript语言的执行环境是"单线程"(single thread).所谓"单线程",就是指一次只 ...

随机推荐

  1. 微信企业号 获取AccessToken

    目录 1. AccessToken介绍 2. 示例代码 1. AccessToken介绍 1.1 什么是AccessToken AccessToken即访问凭证,业务服务器每次主动调用企业号接口时需要 ...

  2. Swift与C#的基础语法比较

    背景: 这两天不小心看了一下Swift的基础语法,感觉既然看了,还是写一下笔记,留个痕迹~ 总体而言,感觉Swift是一种前后端多种语言混合的产物~~~ 做为一名.NET阵营人士,少少多多总喜欢通过对 ...

  3. .NET Core系列 : 2 、project.json 这葫芦里卖的什么药

    .NET Core系列 : 1..NET Core 环境搭建和命令行CLI入门 介绍了.NET Core环境,本文介绍.NET Core中最重要的一个配置文件project.json的相关内容.我们可 ...

  4. ASP.NET路由模型解析

    大家好,我又来吹牛逼了 ~-_-~ 转载请注明出处:来自吹牛逼之<ASP.NET路由模型解析> 背景:很多人知道Asp.Net中路由怎么用的,却不知道路由模型内部的运行原理,今天我就给大家 ...

  5. 【.net 深呼吸】跨应用程序域执行程序集

    应用程序域,你在网上可以查到它的定义,凡是概念性的东西,大伙儿只需要会搜索就行,内容看了就罢,不用去记忆,更不用去背,“名词解释”是大学考试里面最无聊最没水平的题型. 简单地说,应用程序域让你可以在一 ...

  6. 使用Zabbix监控Oracle数据库

    Orabbix介绍 监控Oracle数据库我们需要安装第三方提供的Zabbix插件,我们先测试比较有名的Orabbix,http://www.smartmarmot.com/product/orabb ...

  7. 谈谈一些有趣的CSS题目(八)-- 纯CSS的导航栏Tab切换方案

    开本系列,谈谈一些有趣的 CSS 题目,题目类型天马行空,想到什么说什么,不仅为了拓宽一下解决问题的思路,更涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题 ...

  8. IIC驱动移植在linux3.14.78上的实现和在linux2.6.29上实现对比(deep dive)

    首先说明下为什么写这篇文章,网上有许多博客也是介绍I2C驱动在linux上移植的实现,但是笔者认为他们相当一部分没有分清所写的驱动时的驱动模型,是基于device tree, 还是基于传统的Platf ...

  9. HTML+CSS 项目总结

    在过去的大概一个月的学习,基本掌握了HTML+CSS的用法和特性. 这个星期老师给我们布置了一个PC端的实战项目,并且要求在3-4天内完成,我不惜废寝忘食,在紧迫的时间内大致地完成了,但是有些效果不能 ...

  10. 移动应用App测试与质量管理一

    测试工程师 基于Html的WebApp测试, 现在一些移动App混Html5 HTML5性能测试 兼容性 整理后的脑图 测试招聘 弱化大量技术考察 看重看问题的高度 看重潜力 测试经验 质量管理 专项 ...