Skip to content
medium
this绑定规则

Function.prototype.call

Function.prototype.call可以用来很方便的修改函数的this。

你能实现一个myCall来模拟Function.prototype.call吗?

根据最新的 ECMAScript spec,thisArg 不会被类型转换,在 Strict Mode下也不会被更改为window。

你的代码需要遵从上述逻辑,实现非strict mode的情况。

Function.prototype.call/apply/bind和 Reflect.apply 可以了解,但请不要在这里使用。

题目模版

js
Function.prototype.mycall = function (thisArg, ...args) {

}
ts
declare global {
  interface Function {
    mycall: <T, A extends any[], R>(this: (...args: A) => R, thisArg: T, ...args: A) => R
  }
}

Function.prototype.mycall = function mycall(this: (...args: any[]) => any, thisArg: any, ...args: any[]) {

}

测试代码

js
import { describe, expect, it } from 'vitest'
import './call.js'

describe('function.prototype.mycall', () => {
  it('应该正确调用函数并返回结果', () => {
    function add(a, b) {
      return a + b
    }

    const result = add.mycall(null, 2, 3)
    expect(result).toBe(5)
  })

  it('应该正确设置this上下文', () => {
    function getName() {
      return this.name
    }

    const obj = { name: '张三' }
    const result = getName.mycall(obj)
    expect(result).toBe('张三')
  })

  it('应该处理undefined的thisArg', () => {
    function getThis() {
      return this
    }

    const result = getThis.mycall(undefined)
    expect(result).toBe(window)
  })

  it('应该处理null的thisArg', () => {
    function getThis() {
      return this
    }

    const result = getThis.mycall(null)
    expect(result).toBe(window)
  })

  it('应该将原始类型转换为对象', () => {
    function getType() {
      return typeof this
    }

    const result = getType.mycall('hello')
    expect(result).toBe('object')
  })

  it('应该传递多个参数', () => {
    function multiply(a, b, c) {
      return a * b * c
    }

    const result = multiply.mycall(null, 2, 3, 4)
    expect(result).toBe(24)
  })

  it('应该处理返回对象的函数', () => {
    function createObject(name, age) {
      return { name, age, context: this }
    }

    const thisArg = { id: 1 }
    const result = createObject.mycall(thisArg, '李四', 25)
    expect(result.name).toBe('李四')
    expect(result.age).toBe(25)
    expect(result.context).toBe(thisArg)
  })

  it('应该正确处理传入基本类型的对象', () => {
    const returnThis = function () {
      return this
    }

    expect(typeof returnThis.mycall(1)).toBe('object')
    expect(returnThis.mycall(1).valueOf()).toBe(1)
    expect(typeof returnThis.mycall('1')).toBe('object')
    expect(returnThis.mycall('1').valueOf()).toBe('1')
  })
})
ts
import { describe, expect, it } from 'vitest'
import './call'

describe('function.prototype.mycall', () => {
  it('应该正确调用函数并返回结果', () => {
    function add(a: number, b: number) {
      return a + b
    }

    const result = add.mycall(null, 2, 3)
    expect(result).toBe(5)
  })

  it('应该正确设置this上下文', () => {
    function getName(this: any) {
      return this.name
    }

    const obj = { name: '张三' }
    const result = getName.mycall(obj)
    expect(result).toBe('张三')
  })

  it('应该处理undefined的thisArg', () => {
    function getThis(this: any) {
      return this
    }

    const result = getThis.mycall(undefined)
    expect(result).toBe(window)
  })

  it('应该处理null的thisArg', () => {
    function getThis(this: any) {
      return this
    }

    const result = getThis.mycall(null)
    expect(result).toBe(window)
  })

  it('应该将原始类型转换为对象', () => {
    function getType(this: any) {
      return typeof this
    }

    const result = getType.mycall('hello')
    expect(result).toBe('object')
  })

  it('应该传递多个参数', () => {
    function multiply(a: number, b: number, c: number) {
      return a * b * c
    }

    const result = multiply.mycall(null, 2, 3, 4)
    expect(result).toBe(24)
  })

  it('应该处理返回对象的函数', () => {
    function createObject(this: any, name: string, age: number) {
      return { name, age, context: this }
    }

    const thisArg = { id: 1 }
    const result = createObject.mycall(thisArg, '李四', 25)
    expect(result.name).toBe('李四')
    expect(result.age).toBe(25)
    expect(result.context).toBe(thisArg)
  })

  it('应该正确处理传入基本类型的对象', () => {
    const returnThis = function (this: any) {
      return this
    }

    expect(typeof returnThis.mycall(1)).toBe('object')
    expect(returnThis.mycall(1).valueOf()).toBe(1)
    expect(typeof returnThis.mycall('1')).toBe('object')
    expect(returnThis.mycall('1').valueOf()).toBe('1')
  })
})

答案

类型路径
JS 版本problems/Day 15/answer.js
TS 版本problems/Day 15/ts/answer.ts
Review15.md

内容基于 MIT 许可 | 保持节奏 · 持续积累