# JavaScript 箭头函数
# 前言
;ES6
中的 箭头函数 (opens new window) 作用非常简单,即简化函数且不绑定this
。
内容仅是记录箭头函数的部分特性,包括一些发散和总结,希望对你有用。
# 语法特性
# 没有 this
箭头函数没有this
,它的this
是根据词法作用域派生而来,由上下文决定。
function fn() {
setTimeout(() => {
console.log(this)
})
}
fn() // Window {}
结合babel
转换后的es5
代码,简单概括就是,箭头函数的this
总是指向定义时的上层作用域中的this
。
"use strict"
function fn() {
var _this = this
setTimeout(function () {
console.log(_this)
})
}
fn()
没有this
,call
、apply
或者bind
也就不能改变指向。
var foo = 'outer'
var fn = () => {
console.log(this.foo)
}
fn.call({ foo: 'inner' }) // outer
# 没有 prototype 原型对象
在ECMA-262
规范中的第 20.2.4.3 (opens new window) 小节中。
20.2.4.3 prototype
Function objects created using Function.prototype.bind, or by evaluating a MethodDefinition (that is not a GeneratorMethod or AsyncGeneratorMethod) or an ArrowFunction do not have a "prototype" property.
大致意思就是使用Function.prototype.bind
、箭头函数或者一类特殊方法创建的函数对象都没有prototype
属性。
如果还是不太清楚,可以看以下几个栗子。
function f() { }
f.bind().prototype // undefined
(() => { }).prototype // undefined
const foo = {
method() { }
}
foo.method.prototype // undefined
注意第三种类型中,构造器方法是有prototype
的。
const foo = {
*method() { }
}
foo.method.prototype // Generator {}
const foo = {
async *method() { }
}
foo.method.prototype // AsyncGenerator {}
# 不能作为构造函数
箭头函数不能作为构造函数,或者说不能通过new
关键字调用。
简写一个_new
函数来辅助分析。
function _new(constructor, ...args) {
const result = {}
Object.setPrototypeOf(result, constructor.prototype)
constructor.apply(result, args)
return result
}
若箭头函数可以通过new
调用,首先空对象创建后,要将空对象的原型指向箭头函数Person
的prototype
。另外还要将箭头函数Person
内部的this
指向空对象。
const Person = age => {
this.age = age
}
_new(Person, 12)
而根据文初内容,箭头函数是没有prototype
属性的,并且它的内部this
是无法通过apply
来改变指向的,new
的过程也就无法实现。
此处仅可以辅助说明,箭头函数不能作为构造函数。
你应该了解的是,JavaScript
的函数有[[Call]]
和[[Construct]]
两个内部方法,注意控制台是访问不到不可见的,你可以理解为是浏览器层面的东西,是引擎定义的内部方法。
当函数直接调用时,将执行[[Call]]
方法,运行函数体。而当通过new
调用函数时,将执行[[Construct]]
方法,运行类似刚才_new
函数的一系列操作。
在ECMA262
规范的 20.2.3 (opens new window) 中表明,没有[[Construct]]
内部方法,是不能够作为构造函数的。
20.2.3 Properties of the Function Prototype Object
The Function prototype object:
...
does not have a [[Construct]] internal method; it cannot be used as a constructor with the new operator.
所以最根本
的原因是,浏览器引擎没有赋予箭头函数[[Construct]]
这个内部方法,因此也就不能作为构造函数调用,而作为构造函数所应该具有的那些属性,浏览器也就没必要去实现,都返回undefined
就行了。
# 没有 arguments 实参列表
箭头函数没有arguments
对象。
const foo = first => {
console.log(arguments)
}
foo(1) // Uncaught ReferenceError: arguments is not defined
控制台下,箭头函数作用域内确实没有arguments
对象。
看下普通函数的情况呢。
注意虽然说箭头函数没有arguments
对象,但是它的arguments
由外层最近的非箭头函数决定。
function outer(first) {
var inner = second => {
console.log(arguments) // Arguments [1, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
inner(2)
}
outer(1)
看下作用域情况呢。
另外若想访问箭头函数的实参列表,可以使用剩余参数。
var foo = (...args) => {
console.log(args) // [1]
}
foo(1)
# 没有 new.target
;new.target (opens new window) 用于检测函数是否为new
调用,若通过new
调用,将返回构造函数的引用,而在普通函数中,new.target
返回undefined
。
function F() {
console.log(new.target === F) // true
}
new F()
function fn() {
console.log(new.target) // undefined
}
fn()
箭头函数不能作为构造函数,无法通过new
来调用,而new.target
用来检测是否为new
调用就毫无意义,所以箭头函数必然也没有new.target
。
与arguments
和this
类似,虽然自身没有,但是也是由外层最近的非箭头函数决定。
function Outer() {
console.log(new.target === Outer) // true
function center() {
console.log(new.target) // undefined
const inner = () => {
console.log(new.target) // undefined
}
inner()
}
center()
}
new Outer()
# 没有 super
;super (opens new window) 在子类方法中指向父类原型,在子类静态方法中指向父类。
class Parent {
static func() {
console.log('hello Parent func')
}
constructor() { }
method() {
console.log('hello Parent method')
}
}
class Child extends Parent {
static func() {
super.func()
}
constructor() {
super()
}
method() {
super.method()
}
}
const instance = new Child()
Child.func() // hello Parent func
instance.method() // hello Parent method
另外注意形如super.property = value
的赋值方式,super
等价于this
。
class Parent {
constructor() { }
}
class Child extends Parent {
static func() {
super.func = 3
}
constructor() {
super()
}
method() {
super.func = 99
}
}
const instance = new Child()
Child.func()
instance.method()
instance // Child {func: 99}
Child // class Child {func: 3}
;setPrototypeOf
实现的继承。
const instance = {
func() {
super.func()
}
}
const prototype = {
func() {
console.log('hello world')
}
}
Object.setPrototypeOf(instance, prototype)
instance.func() // hello world
箭头函数没有super
,也是与arguments
类似,虽然自身没有,但是可以由外层最近的非箭头函数决定。
const instance = {
func() {
const fn = () => {
super.func()
}
fn()
}
}
const prototype = {
func() {
console.log('hello world')
}
}
Object.setPrototypeOf(instance, prototype)
instance.func() // hello world
# 不允许重复的参数名
箭头函数不允许形参名重复,无论是否是处在严格模式中,另外注意代码将在解析阶段就会抛出错误。
const f = (first, ...first) => { } // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
# 小结
- 箭头函数没有
this
,它的this
是根据词法作用域派生而来,由上下文决定。总是指向定义时的上层作用域下的this
- 箭头函数没有原型
prototype
- 箭头函数不能作为构造函数,根本原因在于浏览器没有赋予箭头函数
[[Construct]]
内部方法 - 箭头函数没有
arguments
、super
和new.taget
,但是可由外层最近的非箭头函数决定 - 箭头函数不允许形参名重复,无论是否在严格模式下
# 🎉 写在最后
🍻伙伴们,如果你已经看到了这里,觉得这篇文章有帮助到你的话不妨点赞👍或 Star (opens new window) ✨支持一下哦!
手动码字,如有错误,欢迎在评论区指正💬~
你的支持就是我更新的最大动力💪~
GitHub (opens new window) / Gitee (opens new window)、GitHub Pages (opens new window)、掘金 (opens new window)、CSDN (opens new window) 同步更新,欢迎关注😉~