Skip to content

JS 之 call 和 apply 的模拟实现 #43

Open
@myLightLin

Description

@myLightLin

JS 中的 this 一文中,我们提到可以通过 call, apply, bind 显式指定 this,本文就来介绍下 call 和 apply 的模拟实现

call 的模拟实现

call 的基本用法如下:

func.call(thisArg, arg1, arg2, arg3, ...)

它的基本思路就是针对传入的 this 参数临时挂载一个属性,作为方法执行,这样 this 自然就指向调用这个参数了,函数执行完毕后再删掉临时属性,概括起来就是下面这样:

  • thisArg.fn = this
  • thisArg.fn()
  • delete thisArg.fn

来看代码:

Function.prototype.myCall = function(thisArg, ...args) {
  if (typeof this !== 'function')  throw new TypeError('it must be invoke by function')

  if (thisArg == undefined) {
    thisArg = window
  } else {
    thisArg = Object(thisArg)   // 包装成对象
  }

  const func = Symbol('func')  // 创建一个不重复的属性常量
  thisArg[func] = this
  
  const res = thisArg[func](...args)

  delete thisArg[func]
  return res
}

测试使用:

var obj = {
  name: '张三'
}
var name = '李四'
function foo(age) {
  console.log(age)
  console.log(this.name)
}
foo.myCall(obj, 25)

apply 的模拟实现

apply 语法与 call 的区别在于,它的第二个参数接收的是类数组对象,其余跟 call 一样:

func.apply(thisArg, [argsArray])

实现代码如下:

Function.prototype.myApply = function(thisArg, arr) {
  if (typeof this !== 'function')  throw new TypeError('it must be invoke by function')

  if (thisArg == undefined) {
    thisArg = window
  } else {
    thisArg = Object(thisArg)   // 包装成对象
  }

  function isArrayLike(obj) {
    return obj && typeof obj === 'object' && 'length' in obj
  }

  const func = Symbol('func')  // 创建一个不重复的属性常量
  thisArg[func] = this

  let result
  if (arr) {
    if (!Array.isArray(arr) && !isArrayLike(arr)) throw new Error('the second  params must be array or array-like')
    else {
      const args = Array.from(arr)
      result = thisArg[func](...args)
    }
  } else {
    result = thisArg[func]()
  }
  
  delete thisArg[func]
  
  return result
}

测试代码如下:

var obj = {
  name: '张三'
}
var name = '李四'
function foo() {
  console.log(this.name)
}
foo.myApply(obj)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions