# 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()

  没有thiscallapply或者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调用,首先空对象创建后,要将空对象的原型指向箭头函数Personprototype。另外还要将箭头函数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

  与argumentsthis类似,虽然自身没有,但是也是由外层最近的非箭头函数决定。

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]]内部方法
  • 箭头函数没有argumentssupernew.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) 同步更新,欢迎关注😉~

最后更新时间: 11/11/2022, 10:47:14 PM