JS之箭头函数
箭头函数是ES6引入到JavaScript中的,是一种新式的匿名函数的写法,类似于其他语言中Lambda函数。箭头函数和传统函数有很多的不同,例如作用域、语法写法等等。
一、传统函数的定义
1、普通函数定义
下面是一个sum函数的定义,可以返回两个参数之和。
function sum(a, b) {
return a + b
}
对于传统函数,你甚至可以在定义之前调用该函数
sum(1, 2)
function sum(a, b) {
return a + b
}
你可以通过函数名,打印出其函数申明
console.log(sum)
输出结果如下:
ƒ sum(a, b) {
return a + b
}
函数表达式通常可以有个名字,但是也可以是匿名的,意味着函数是没有名字的。
2、匿名函数
下面例子是sum函数的匿名申明方式:
const sum = function (a, b) {
return a + b
}
这时我们将匿名函数赋值给了sum变量,如果这时候在定义之前调用会导致错误:
sum(1, 2)
const sum = function (a, b) {
return a + b
}
错误返回:Uncaught ReferenceError: Cannot access 'sum' before initialization
我们也可以把sum打印出来:
const sum = function (a, b) {
return a + b
}
console.log(sum)
打印出来的结果如下:
ƒ (a, b) {
return a + b
}
sum是一个匿名函数,而不是一个有名字的函数。
二、箭头函数
箭头函数和普通函数有非常多的不同,箭头函数没有自己的this绑定,没有prototype,不能被用做构造函数。同时,箭头函数可以作为普通函数提供更为简洁的写法。
1、箭头函数定义
写法非常简洁:
const sum = (a, b) => { return a + b }
可以简化函数声明,并且提供隐含返回值:
const sum = (a, b) => a + b
当只有一个参数的时候,可以省略(),写成如下形式:
const square = x => x * x
2、this绑定
在JavaScript中,this往往是一个比较复杂诡异的事情。在JavaScript中有bind、apply、call等方法会影响this所指定的对象。
箭头函数的this是语义层面的,因此,箭头函数中的this是由上下文作用域决定的。
下面的例子解释this在普通函数和箭头函数的区别:
const printNumbers = {
phrase: 'The current value is:',
numbers: [1, 2, 3, 4],
loop() {
this.numbers.forEach(function (number) {
console.log(this.phrase, number)
})
},
}
你也许会期望loop函数会打印出文本和对应的数字,然后真正返回的内容其实是undefined:
printNumbers.loop()
复制代码
输出:
undefined 1
undefined 2
undefined 3
undefined 4
复制代码
在上面的例子中,this.phrase代表了undefined,说明在forEach方法中的匿名函数中的this并不会指向printNumber对象。这是因为普通函数并不是通过上下文作用域来决定this的值,而是通过实际调用函数的对象来决定的。
在老版本的JavaScript,你可以通过bind方法来显示的绑定this。使用bind的方式如下:
const printNumbers = {
phrase: 'The current value is:',
numbers: [1, 2, 3, 4],
loop() {
// 将外部的printNumber对象绑定到内部forEach函数中的'this'
this.numbers.forEach(
function (number) {
console.log(this.phrase, number)
}.bind(this),
)
},
}
printNumbers.loop()
这将输出正确的结果:
The current value is: 1
The current value is: 2
The current value is: 3
The current value is: 4
箭头函数提供了一个更为优雅的解决方案,由于其this的指向是由上下文作用域来决定的,因此它会指向printNumbers对象:
const printNumbers = {
phrase: 'The current value is:',
numbers: [1, 2, 3, 4],
loop() {
this.numbers.forEach((number) => {
console.log(this.phrase, number)
})
},
}
printNumbers.loop()
上面箭头函数在循环中可以很好的使用,但是作为对象方法却会造成问题。如下例:
const printNumbers = {
phrase: 'The current value is:',
numbers: [1, 2, 3, 4],
loop: () => {
this.numbers.forEach((number) => {
console.log(this.phrase, number)
})
},
}
复制代码
调用loop方法
printNumbers.loop()
复制代码
结果会出现如下错误:
Uncaught TypeError: Cannot read property 'forEach' of undefined
复制代码
这是因为在loop箭头函数申明的时候,this指向并不会是printNumbers对象,而是外部的Window。而Window上面并没有numbers字段,从而导致错误。因此,对象方法一般使用传统方法。
3、箭头函数没有Prototype和构造函数
在JavaScript中的Function或者Class中都会有一个Prototype属性,这个可以用于对象拷贝或者继承。
function myFunction() {
this.value = 5
}
// Log the prototype property of myFunction
console.log(myFunction.prototype)
复制代码
输出:
{constructor: ƒ}
但是,箭头函数是不存在Prototype属性,我们可以尝试打印出箭头函数的Prototype。
const myArrowFunction = () => {}
// Attempt to log the prototype property of myArrowFunction
console.log(myArrowFunction.prototype)
输出:
undefined
同时,由于没有Prototype属性,因此也没法通过new进行实例化。
const arrowInstance = new myArrowFunction()
console.log(arrowInstance)
输出错误:
Uncaught TypeError: myArrowFunction is not a constructor
总结:
箭头函数始终是匿名的,没有Prototype或者构造函数,无法用new进行实例化,并且是通过语义上下文来决定this指向。