咱们先来回顾下箭头函数的基本语法。html
ES6 增长了箭头函数:git
let func = value => value; 复制代码
至关于:es6
let func = function (value) { return value; }; 复制代码
若是须要给函数传入多个参数:github
let func = (value, num) => value * num; 复制代码
若是函数的代码块须要多条语句:express
let func = (value, num) => { return value * num }; 复制代码
若是须要直接返回一个对象:markdown
let func = (value, num) => ({total: value * num}); 复制代码
与变量解构结合:app
let func = ({value, num}) => ({total: value * num}) // 使用 var result = func({ value: 10, num: 10 }) console.log(result); // {total: 100} 复制代码
不少时候,你可能想不到要这样用,因此再来举个例子,好比在 React 与 Immutable 的技术选型中,咱们处理一个事件会这样作:异步
handleEvent = () => { this.setState({ data: this.state.data.set("key", "value") }) }; 复制代码
其实就能够简化为:函数
handleEvent = () => { this.setState(({data}) => ({ data: data.set("key", "value") })) }; 复制代码
本篇咱们重点比较一下箭头函数与普通函数。oop
主要区别包括:
箭头函数没有 this,因此须要经过查找做用域链来肯定 this 的值。
这就意味着若是箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 this。
模拟一个实际开发中的例子:
咱们的需求是点击一个按钮,改变该按钮的背景色。
为了方便开发,咱们抽离一个 Button 组件,当须要使用的时候,直接:
// 传入元素 id 值便可绑定该元素点击时改变背景色的事件 new Button("button") 复制代码
HTML 代码以下:
<button id="button">点击变色</button> 复制代码
JavaScript 代码以下:
function Button(id) { this.element = document.querySelector("#" + id); this.bindEvent(); } Button.prototype.bindEvent = function() { this.element.addEventListener("click", this.setBgColor, false); }; Button.prototype.setBgColor = function() { this.element.style.backgroundColor = '#1abc9c' }; var button = new Button("button"); 复制代码
看着好像没有问题,结果倒是报错 Uncaught TypeError: Cannot read property 'style' of undefined
这是由于当使用 addEventListener() 为一个元素注册事件的时候,事件函数里的 this 值是该元素的引用。
因此若是咱们在 setBgColor 中 console.log(this)
,this 指向的是按钮元素,那 this.element 就是 undefined,报错天然就理所固然了。
也许你会问,既然 this 都指向了按钮元素,那咱们直接修改 setBgColor 函数为:
Button.prototype.setBgColor = function() { this.style.backgroundColor = '#1abc9c' }; 复制代码
不就能够解决这个问题了?
确实能够这样作,可是在实际的开发中,咱们可能会在 setBgColor 中还调用其余的函数,好比写成这种:
Button.prototype.setBgColor = function() { this.setElementColor(); this.setOtherElementColor(); }; 复制代码
因此咱们仍是但愿 setBgColor 中的 this 是指向实例对象的,这样就能够调用其余的函数。
利用 ES5,咱们通常会这样作:
Button.prototype.bindEvent = function() { this.element.addEventListener("click", this.setBgColor.bind(this), false); }; 复制代码
为避免 addEventListener 的影响,使用 bind 强制绑定 setBgColor() 的 this 为实例对象
使用 ES6,咱们能够更好的解决这个问题:
Button.prototype.bindEvent = function() { this.element.addEventListener("click", event => this.setBgColor(event), false); }; 复制代码
因为箭头函数没有 this,因此会向外层查找 this 的值,即 bindEvent 中的 this,此时 this 指向实例对象,因此能够正确的调用 this.setBgColor 方法, 而 this.setBgColor 中的 this 也会正确指向实例对象。
在这里再额外提一点,就是注意 bindEvent 和 setBgColor 在这里使用的是普通函数的形式,而非箭头函数,若是咱们改为箭头函数,会致使函数里的 this 指向 window 对象 (非严格模式下)。
最后,由于箭头函数没有 this,因此也不能用 call()、apply()、bind() 这些方法改变 this 的指向,能够看一个例子:
var value = 1; var result = (() => this.value).bind({value: 2})(); console.log(result); // 1 复制代码
箭头函数没有本身的 arguments 对象,这不必定是件坏事,由于箭头函数能够访问外围函数的 arguments 对象:
function constant() { return () => arguments[0] } var result = constant(1); console.log(result()); // 1 复制代码
那若是咱们就是要访问箭头函数的参数呢?
你能够经过命名参数或者 rest 参数的形式访问参数:
let nums = (...nums) => nums; 复制代码
JavaScript 函数有两个内部方法:[[Call]] 和 [[Construct]]。
当经过 new 调用函数时,执行 [[Construct]] 方法,建立一个实例对象,而后再执行函数体,将 this 绑定到实例上。
当直接调用的时候,执行 [[Call]] 方法,直接执行函数体。
箭头函数并无 [[Construct]] 方法,不能被用做构造函数,若是经过 new 的方式调用,会报错。
var Foo = () => {}; var foo = new Foo(); // TypeError: Foo is not a constructor 复制代码
由于不能使用 new 调用,因此也没有 new.target 值。
关于 new.target,能够参考 es6.ruanyifeng.com/#docs/class…
因为不能使用 new 调用箭头函数,因此也没有构建原型的需求,因而箭头函数也不存在 prototype 这个属性。
var Foo = () => {}; console.log(Foo.prototype); // undefined 复制代码
连原型都没有,天然也不能经过 super 来访问原型的属性,因此箭头函数也是没有 super 的,不过跟 this、arguments、new.target 同样,这些值由外围最近一层非箭头函数决定。
最后,关于箭头函数,引用 MDN 的介绍就是:
An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.
翻译过来就是:
箭头函数表达式的语法比函数表达式更短,而且不绑定本身的this,arguments,super或 new.target。这些函数表达式最适合用于非方法函数(non-method functions),而且它们不能用做构造函数。
那么什么是 non-method functions 呢?
咱们先来看看 method 的定义:
A method is a function which is a property of an object.
对象属性中的函数就被称之为 method,那么 non-mehtod 就是指不被用做对象属性中的函数了,但是为何说箭头函数更适合 non-method 呢?
让咱们来看一个例子就明白了:
var obj = { i: 10, b: () => console.log(this.i, this), c: function() { console.log( this.i, this) } } obj.b(); // undefined Window obj.c(); // 10, Object {...} 复制代码
自执行函数的形式为:
(function(){ console.log(1) })() 复制代码
或者
(function(){ console.log(1) }()) 复制代码
利用箭头简化自执行函数的写法:
(() => { console.log(1) })() 复制代码
可是注意:使用如下这种写法却会报错:
(() => { console.log(1) }()) 复制代码
为何会报错呢?嘿嘿,若是你知道,能够告诉我~
ES6 系列目录地址:github.com/mqyqingfeng…
ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级做用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。
若是有错误或者不严谨的地方,请务必给予指正,十分感谢。若是喜欢或者有所启发,欢迎 star,对做者也是一种鼓励。