Function

coderljw 2024-10-13 JS 基础
  • JS 基础
  • Function
大约 6 分钟

# 1. Function(args..., functionBody)

  • 构造函数创建匿名函数。前面参数会成为匿名函数的形参,最后一个参数会成为匿名函数的可执行体(Vue 中的 render 函数的形成方式)。
Function("console.log('执行代码')")() // => '执行代码'

Function('name', 'console.log(name)')('徐扶墙') // => '徐扶墙'
1
2
3
  • 模板处理函数。
function foo(tpl, ...values) {
  console.log(tpl) // => ['try call ', ' ', '', raw: Array(3)]
  console.log(values) // => ['foo', '徐扶墙']
}

foo`try call ${foo.name} ${'徐扶墙'}`
1
2
3
4
5
6
  • 自执行匿名函数。
// prettier-ignore
(function () {
  return '徐扶墙'
}())

(function () {
  return '徐扶墙'
})()

void (function () {
  console.log('徐扶墙')
})()
1
2
3
4
5
6
7
8
9
10
11
12

# 2. getter & setter

  • 获取/设置对象属性值时触发的函数。

    可以结合使用 getter 和 setter 来创建一个伪属性。不可能在具有真实值的属性上同时拥有一个 getter 或 setter 器(MDN)。

const foo = {
  name: '徐扶墙',
  age: 17,
  harem: [],
  get firstWife() {
    return this.harem[0] || '裆下有些忧郁呀!'
  },
  set marryWife(wife) {
    this.harem.push(wife)
  },
}

foo.marryWife = '姜泥'
foo.marryWife = '裴南苇'
foo.firstWife // => '姜泥'
foo // => {name: '徐扶墙', age: 17, harem: ['姜泥', '裴南苇']}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 3. new.target

  • new.target 属性允许你检测函数或构造方法是否是通过 new 运算符被调用的。
function Foo(name) {
  if (!new.target) throw new Error('must be called with new')
  this.name = name
}

Foo() // => Uncaught Error: must be called with new

const bar = new Foo('徐扶墙') // => {name: '徐扶墙'}
1
2
3
4
5
6
7
8

# 4. arguments

  • 保存所有传递的参数(是一个伪数组)。
function foo() {
  console.log(arguments) // => Arguments(2) ['徐扶墙', '裴南苇', length: 2]
}

foo('徐扶墙', '裴南苇')
1
2
3
4
5

# 5. call(this, arg...) & apply(this, [arg...]) & bind(this, arg...)

  • 第一个参数均为 this 的指向。call、bind 的参数传递通过逗号分割,apply 的参数通过数组传递。

  • bind 不会直接执行,会返回一个函数,参数可分别传递(bind 是柯里化函数)。

  • 若 this 为 undefined 或 null,则指向全局。若为 [number | string | boolean],则会隐式使用构造函数包裹后返回,俗称装箱。

const foo = {
  name: '徐扶墙',
  say(arg1, arg2) {
    console.log(`${this.name}${arg1}${arg2}${foo.name}`)
  },
}
const bar = {
  name: '裴南苇',
}

foo.say.call(bar, '天下', '第二')
foo.say.apply(bar, ['天下', '第二'])
foo.say.bind(bar, '天下')('第二')
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 反柯里化函数。

    Function.prototype.call.apply(self, [obj, ...args]) 相当于 self.call(obj, ...args) 相当于 obj.self(...args)。转换到下方代码示意为 foo.push(6)。

    Function.prototype.uncurrying = function () {
      const self = this
      return function () {
        return Function.prototype.call.apply(self, arguments)
      }
    }
    
    Array.push = Array.prototype.push.uncurrying()
    
    const foo = {
      0: 4,
      1: 3,
      2: 9,
      length: 3,
    }
    
    Array.push(foo, 6) // foo: {0: 4, 1: 3, 2: 9, 3: 6, length: 4}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# 6. Generator

  • function* 这种声明方式(function 关键字后跟一个星号)会定义一个生成器函数 (generator function),它返回一个 Generator 对象。生成器函数在执行时能暂停,后面又能从暂停处继续执行。

    function* foo() {
      yield 'jack'
      yield 'pony'
      return 'coderljw'
    }
    
    for (const res of foo()) console.log(res) // 依次打印:'jack' -> 'pony'
    
    function* bar() {
      yield* foo()
      yield 'Evan You'
      yield 'Dan'
      return 'coderljw'
    }
    
    for (const res of bar()) console.log(res) // 依次打印:'jack' -> 'pony' -> 'Evan You' -> 'Dan'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  • next。

    function* foo(arg) {
      console.log(arg) // => 'nothing'
      const Alibaba = yield 'jack'
      console.log(Alibaba) // => 'Alibaba'
      const Tencent = yield 'pony'
      console.log(Tencent) // => 'Tencent'
      return 'coderljw'
    }
    
    const gen = foo('nothing')
    gen.next() // => {value: 'jack', done: false}
    gen.next('Alibaba') // => {value: 'pony', done: false}
    gen.next('Tencent') // => {value: 'coderljw', done: true}
    gen.next() // => {value: undefined, done: true}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • return 会结束生成器函数。

    function* foo() {
      const Alibaba = yield 'jack'
      const Tencent = yield 'pony'
      return 'coderljw'
    }
    
    const gen = foo()
    gen.next() // => {value: 'jack', done: false}
    gen.return('Evan You') // => {value: 'Evan You', done: true}
    gen.next() // => {value: undefined, done: true}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • throw 会结束生成器函数并抛出错误,可用 try...catch 捕获错误。

    • 内部捕获。
    function* foo() {
      try {
        const Alibaba = yield 'jack'
      } catch (err) {
        console.log(err) // => 4396
      }
      const Tencent = yield 'pony'
      return 'coderljw'
    }
    
    const gen = foo()
    gen.next() // => {value: 'jack', done: false}
    const res = gen.throw('4396')
    console.log(res) // => {value: 'pony', done: false}
    gen.next() // => {value: 'coderljw', done: true}
    gen.next() // => {value: undefined, done: true}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    • 外部捕获。
    function* bar() {
      const Alibaba = yield 'jack'
      const Tencent = yield 'pony'
      return 'coderljw'
    }
    
    const gen = bar()
    gen.next() // => {value: 'jack', done: false}
    try {
      gen.throw(new Error('2200'))
    } catch (err) {
      console.log(err) // => Error: 2200
    }
    gen.next() // => {value: undefined, done: true}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • 实现状态机。

    function* foo() {
      while (true) {
        yield 'on'
        yield 'off'
      }
    }
    
    const gen = foo()
    gen.next() // => {value: 'on', done: false}
    gen.next() // => {value: 'off', done: false}
    gen.next() // => {value: 'on', done: false}
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • 搭配 Promise 函数(嘿嘿!另一种形式的组合函数)。

    function foo(ms) {
      return new Promise((resolve, reject) => {
        setTimeout(_ => resolve(4396), ms)
      })
    }
    
    function bar(ms) {
      return new Promise((resolve, reject) => {
        setTimeout(_ => resolve(1557), ms)
      })
    }
    
    function* hoge(promises, res) {
      let n = 0
      while (n < promises.length && n < 4) {
        res = yield promises[n](res)
        n++
      }
    
      return res
    }
    
    function co(gen) {
      return new Promise((resolve, reject) => {
        function next(data) {
          const result = gen.next(data)
          if (result.done) return resolve(result.value)
          result.value.then(
            data => next(data),
            err => reject(err),
          )
        }
        next()
      })
    }
    
    co(hoge([foo, bar], 2200)).then(console.log) // => 1557
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37

# 7. 继承

  • 原型链继承。

    存在引用值共享问题。

    function Super() {
      this.harm = [4, 3, 9]
    }
    Super.prototype.getHarm = function () {
      return this.harm
    }
    
    function Sub() {}
    // 继承 Super
    Sub.prototype = new Super()
    
    const clearlove7 = new Sub()
    const xiaohu = new Sub()
    
    clearlove7.harm.push(6)
    
    clearlove7.getHarm() // => [4, 3, 9, 6]
    xiaohu.getHarm() // => [4, 3, 9, 6]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
  • 盗用构造函数继承(对象伪装/经典继承)。

    解决了引用值共享问题,但不能访问父类原型上的方法。

    function Super() {
      this.harm = [4, 3, 9]
    }
    Super.prototype.getHarm = function () {
      return this.harm
    }
    
    function Sub() {
      Super.call(this)
    }
    
    const clearlove7 = new Sub()
    const xiaohu = new Sub()
    
    clearlove7.harm.push(6)
    
    clearlove7.getHarm() // => Uncaught TypeError
    xiaohu.getHarm() // => Uncaught TypeError
    clearlove7.harm // => [4, 3, 9, 6]
    xiaohu.harm // => [4, 3, 9]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  • 组合继承(伪经典继承)。

    弥补了原型链继承和盗用构造函数继承的不足,但存在效率问题,最主要的效率问题就是父类构造函数始终会被调用两次。

    function Super() {
      this.harm = [4, 3, 9]
    }
    Super.prototype.getHarm = function () {
      return this.harm
    }
    
    function Sub() {
      // 初始化(ES6 super)
      Super.call(this) // 第二次调用 Super()
    }
    // 继承原型
    Sub.prototype = new Super() // 第一次调用 Super()
    
    const clearlove7 = new Sub()
    const xiaohu = new Sub()
    
    clearlove7.harm.push(6)
    
    clearlove7.getHarm() // => [4, 3, 9, 6]
    xiaohu.getHarm() // => [4, 3, 9]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
  • 原型式继承。

    function object(o) {
      function F() {}
      F.prototype = o
      return new F()
    }
    
    const Super = {
      harm: [4, 3, 9],
    }
    
    const clearlove7 = object(Super)
    const xiaohu = object(Super)
    
    clearlove7.harm.push(6)
    
    clearlove7.harm // => [4, 3, 9, 6]
    xiaohu.harm // => [4, 3, 9, 6]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  • 寄生式继承。

    • 寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。
    • 寄生式继承同样适合主要关注对象,而不在乎类型和构造函数的场景。 object() 函数不是寄生式继承所必需的,任何返回新对象的函数都可以在这里使用。
    function object(o) {
      function F() {}
      F.prototype = o
      return new F()
    }
    
    function createAnother(original) {
      // 通过调用函数创建一个新对象
      const clone = object(original)
      // 以某种方式增强这个对象
      clone.getHarm = function () {
        console.log(this.harm)
      }
      return clone
    }
    
    const Super = {
      harm: [4, 3, 9],
    }
    
    const clearlove7 = createAnother(Super)
    const xiaohu = createAnother(Super)
    
    clearlove7.harm.push(6)
    
    clearlove7.getHarm() // => [4, 3, 9, 6]
    xiaohu.getHarm() // => [4, 3, 9, 6]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
  • 寄生式组合继承。

    • 寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。
    • 寄生式组合继承可以算是引用类型继承的最佳模式。
    /*
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    
    《JavaScript 高级程序设计(第四版)》写法,等同于上面两行代码。
      function object(o) {
        function F() {}
        F.prototype = o
        return new F()
      }
    
      function inheritPrototype(subType, superType) {
        const prototype = object(superType.prototype) // 创建对象
        prototype.constructor = subType // 增强对象
        subType.prototype = prototype // 赋值对象
      }
    
      inheritPrototype(Sub, Super)
    */
    
    function Super() {
      this.harm = [4, 3, 9]
    }
    Super.prototype.getHarm = function () {
      return this.harm
    }
    
    function Sub() {
      // 初始化(ES6 super)
      Super.call(this)
    }
    
    // 继承原型
    Sub.prototype = Object.create(Super.prototype)
    // 纠正 constructor 指向
    Sub.prototype.constructor = Sub
    
    const clearlove7 = new Sub()
    const xiaohu = new Sub()
    
    clearlove7.harm.push(6)
    
    clearlove7.getHarm() // => [4, 3, 9, 6]
    xiaohu.getHarm() // => [4, 3, 9]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
以父之名
周杰伦.mp3