js的call、apply、bind方法的使用与实现

89 阅读3分钟

一、用法

call,apply,bind这三个方法其实都是js函数原型上的方法,其功能都是用来改变函数内部this的指向。call,apply方法会立即执行,call的参数需一个一个传,而apply参数以数组方式传。bind这个方法特殊一点,参数需一个一个参,但是其并不会立即调用,而是返回一个函数,并且该函数可作为构造函数使用。当该返回的函数作为构造函数使用时,其内部this指向这个构造函数new出来的实例对象。

  1. call方法的使用
window.name = '李四'
function test(a, b) {
  console.log(this.name, a, b)
}
const obj = {
  name: '张三'
}
test.call(obj, '1', '2') // 张三 1 2 此时this指向obj
test('1', '2') // 李四 1 2 浏览器环境下this指向window
  1. apply方法的使用
function test(...arg) {
  console.log(this.name, ...arg) // 张三 1 2
}
const obj = {
  name: '张三'
}
test.apply(obj, ['1', '2']) // 参数以数组方式传递
  1. bind方法的使用
// 1、返回的函数作为普通函数使用
function test(a, b, c) {
  console.log(this.name, a, b, c) // 张三 1 2 (此时this指向你传进来的obj)
}
const obj = {
  name: '张三'
}
test.bind(obj, '1', '2')('3')


// 2、返回的函数作为普通函数使用
function test1(a, b, c, name) {
  this.name = name
  console.log(this.name, a, b, c) // 王五 1 2 (此时this指向返回的构造函数的实例)
}
const obj1 = {
  name: '张三'
}
const S = test1.bind(obj1, '1', '2', '3')

const s = new S('王五')

二、实现

1、实现call方法

window.name = '李四'
Function.prototype.myCall = function (obj, ...arg) {
  obj = obj || window
  key = Symbol('key') // 避免与obj的属性相同,将其覆盖
  obj[key] = this // 拿到调用的函数,并赋值给obj
  obj[key](...arg)
  delete obj[key] // 运行完删除属性,避免obj无故新增属性
}

function test(a, b) {
  console.log(this.name, a, b)
}
const obj = {
  name: '张三'
}
test.myCall(obj, '1', '2') // 张三 1 2
test('1', '2') // 李四 1 2
  1. 实现apply方法
Function.prototype.myApply = function (obj, arg) {
  obj = obj || window
  key = Symbol('key') // 避免与obj的属性相同,将其覆盖
  obj[key] = this // 拿到调用的函数,并赋值给obj
  obj[key](...arg)
  delete obj[key] // 运行完删除属性,避免obj无故新增属性
}

function test(a, b) {
  console.log(this.name, a, b)
}
const obj = {
  name: '张三'
}
test.myApply(obj, ['1', '2']) // 张三 1 2

3、实现bind方法

Function.prototype.myBind = function (obj, ...arg) {
  obj = obj || window
  let key = Symbol('key')
  obj[key] = this
  const _this = this
  return function (...Arg) {
    let arr = [...arg, ...Arg]
    if(this instanceof _this) { // 重点:这里是判断该函数是否是当构造函数使用
      let newKey = Symbol('newKey')
      this[newKey] = _this
      this[newKey](...arr)
      delete this[newKey]
    }else {
      obj[key](...arr)
      delete obj[key]
    }
  }
}

// 1、返回的函数作为普通函数使用
function test(a, b, c) {
  console.log(this.name, a, b, c) // 张三 1 2
}
const obj = {
  name: '张三'
}
test.myBind(obj, '1', '2')('3')


// 2、返回的函数作为普通函数使用
function test1(a, b, c, name) {
  this.name = name
  console.log(this.name, a, b, c) // 张三 1 2
}
const obj1 = {
  name: '张三'
}
const S = test1.myBind(obj1, '1', '2', '3')

const s = new S('王五')

总结:

call,bind,apply都是用来改变函数内部this指向的,只是在使用方式上有点不同而已。比较特殊的是bind,他不会立即执行,返回的函数既可以当普通函数,又可以当构造函数使用。