箭头函数
# 箭头函数
箭头函数可以用于解决this混乱的问题,而this的问题在es5中真的是很让人恶心的东西
简写
- 如果有且仅有一个参数,()也可以不写
- 如果有且仅有一个语句,return,{}也可以不写
# 基本用法
let func = v => v
// <!-- <====> -->
function func(v){
return v
}
2
3
4
5
如果箭头函数不需要参数或者需要多个参数,就可以
let sum = (a,b,c) => a+b+c
// ES5
function sum(a,b,c){
return a+b+c
}
2
3
4
5
如果箭头函数的代码块多余一条语句,就要使用大括号将他们括起来,并且使用return
语句返回
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
let getTempItem = id => { id: id, name: "Temp" } // error
// 正确的写法
let getTempItem = id => ({ id: id, name: "Temp" })
<!-- <====> -->
function getTempItem (id) {
return { id: id, name: "Temp" }
}
// 有一个特殊的情况需要注意,
let foo = () => { a: 1 } // 不报错,但是得不到想要的值
foo() // undefined
2
3
4
5
6
7
8
9
10
11
上面foo
中,原始意图是返回一个对象{ a: 1 },但是由于引擎认为大括号是代码块,所以执行了一行语句a: 1。这时,a可以被解释为语句的标签,因此实际执行的语句是1;,然后函数就结束了,没有返回值。
至于() => {}
的应用那就太多太多了,相信用过的话都应该会比较喜欢的
# 使用注意点
箭头函数使用又几个使用注意点
- 函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象,这一点很重要,在使用的时候这一点大多数时候会给你带来好处 - 不可以当作构造函数,也就是说,不可以使用
new
命名,否则会抛出错误 - 不可以使用
arguments
对象,该对象在函数体内不存在,如果要是使用,可以使用rest
参数数组代替 - 不可以使用
yield
命令,因此箭头函数不能用作Generator
函数。
解释一下上面的原因
- this对象的指向是可变的,但是在箭头函数中,它是固定的。
function foo() {
setTimeout(() => {
console.log('id:', this.id); // 这个地方this如果不使用call就是指向window,输出21
}, 100);
}
var id = 21;
foo.call({ id: 42 });
2
3
4
5
6
7
8
9
function Timer () {
this.s1 = 0;
this.s2 = 0;
setInterval(() => this.s1++,1000) // 这段代码中,此处的this指向Timer的实例上的变量也就是timer
setInterval(function(){
this.s2++ // 这个地方的this指向window
},1000)
}
var s2 = 0
var timer = new Timer()
setTimeout(() => {console.log(`s1: ${timer.s1}`),3100})
setTimeout(() => {console.log(`s2: ${timer.s2}`),3100}) // 打印的s2根本就没有走过第二个计时器
2
3
4
5
6
7
8
9
10
11
12
- 为什么
() => {}
不能作为构造函数,就是因为this
的原因
let handle = {
id:'asfhqj3rqlfhasldfqhl3r2q3jh4234',
init:function () {
document.addEventListener('click',event => this.doSomeThing(event.type)) // 这里的this指向handle,调用deSomeThing方法
},
doSomeThing: function (type) {
console.log(`Handling ${type} for ${this.id}`)
}
}
2
3
4
5
6
7
8
9
this
指向的固定化,并不是因为箭头函数内部有绑定this
的机制,实际原因是箭头函数根本没有自己的this
,导致内部的this
就是外层代码块的this
。正是因为它没有this
,所以也就不能用作构造函数。这也是我们所说的this
会窜出去
的原因
function foo(){
return () => {
return () => {
return () => {
console.log(`id: ${this.id}`)
}
}
}
}
var f = foo.call({id:1}) // 对于f来讲就在此时此刻已经定下来this就是{id:123}
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
// 在下面三个函数执行之前this就已经窜出去指向第一个f了
2
3
4
5
6
7
8
9
10
11
12
13
14
15
除了this
,还有三个变量在箭头函数之中也是不存在的 arguments
、super
、new.target
(function() {
return [
(() => this.x).bind({ x: 'inner' })()
// (() => this.x))()
];
}).call({ x: 'outer' });
// ['outer']
2
3
4
5
6
7
上面的代码,因为箭头函数是没有this
的,所以bind是没有效果的,this
会窜出到return
外面一层
# 不适合使用箭头函数的场合
- 定义对象的方法,不要使用箭头函数
const cat = {
lives: 9,
jumps: () => {
this.lives--; // 这个地方的this指向的是全局作用域
}
}
2
3
4
5
6
- 需要动态
this
的时候,不要使用箭头函数
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on'); // 这个地方的this指向的是全局作用域,而不是button
});
2
3
4
# 嵌套的箭头函数
箭头函数的嵌套我觉得就是书写简单,增强可读性,见仁见智吧
function insert(value) {
return { into: function (arr) {
return { after: function (afterValue) {
arr.splice(arr.indexOf(afterValue + 1), 0, value)
return arr
}
}
}
}
}
insert(2).into([1,3]).after(1) // [1,2,3]
2
3
4
5
6
7
8
9
10
11
使用es6的箭头函数的书写
let insert = value => ({
into: arr => ({
after: afterValue => {
arr.splice(arr.indexOf(afterValue+1), 0, value)
return arr
}
})
})
insert(2).into([1,3]).after(1) // [1,2,3]
2
3
4
5
6
7
8
9
10
效果是一样的
管道pipeline
机制
const pipeline = (...funcs) =>
val => funcs.reduce((a,b) => b(a),val)
const plus1 = a => a + 1
const plus2 = a => a * 2
const add = pipeline(plus1,plus2)
add(5) // 12
2
3
4
5
6
讲道理,上面的代码有点装X,下面的写法还是朴实无华的
const plus1 = a => a + 1
const plus2 = a => a * 2
const muilt = val => plus2(plus2(val))
2
3
接下来要说的与箭头函数无关,而是说的一种很高效的方法或者说模式吧