bind、call、apply 区别?如何实现一个bind?

992 阅读3分钟

为什么要使用bind,apply,call

简单的来说就是改变this指向,他们都属于函数的方法,所以只有函数才可以调用bind,call,apply

apply

apply接受两个参数,第一个参数是要指向的this对象,第二个参数是函数接收的参数,以数组的方式传入(可以把a想成Array)。 改变this指向后原函数立即执行,且只是临时改变一次this指向

let a = {name: 'xl'}
let b = {
    name: 'xw',
    sayName: function () {
        console.log(this.name)
    }
}
b.sayName.apply(a)  // 'xl'

注意: 当传第一个参数是null或者undefined的时候,默认指向window 浏览器下

fn.apply(null,[1,2]); // this指向window
fn.apply(undefined,[1,2]); // this指向window

实现apply

Function.prototype.myApply = function (context, args) {
    context = context || window
    const fn = Symbol() // 用Symbol来避免fn覆盖context上的属性
    context[fn] = this // this指向调用myApply的函数
    const result = context[fn](...args) // 通过上下文调用函数
    delete context[fn]
    return result
}

call

call接收多个参数,第一个参数是要指向的this对象,函数接收多个参数,以分隔的字符串形式传入。和apply一样改变this指向后原函数立即执行,且只是临时改变一次this指向

function fn(...args){
    console.log(this,args);
}
let obj = {
    myname:"张三"
}

fn.call(obj,1,2); // this会变成传入的obj
fn(1,2) // this指向window

注意: 当传第一个参数是null或者undefined的时候,默认指向window 浏览器下

fn.apply(null,[1,2]); // this指向window
fn.apply(undefined,[1,2]); // this指向window

实现call

Function.prototype.myCall = function (context, ...args) {
   context = context || window
   const fn = Symbol()
   context[fn] = this
   const result = context[fn](...args)
   delete context[fn]
   return result
}

bind

bind和call相似,第一个参数是this指向,传入的也是一个参数列表 bind不会立即执行,而是返回一个改变this指向后的函数的拷贝,参数可以分多次传入

function fn(...args){
    console.log(this,args);
}
let obj = {
    myname:"张三"
}
fn.bind(obj,'z')('w') // {myname: "张三"}  ["z", "w"]

实现一个bind

Function.prototype.myBind = function (context, ...args) {
    context = context || window
    const fn = this // this指向调用myBind的函数
    // 返回当前函数的引用
    return function (...newArgs) {
        return fn.myApply(context, args.concat(newArgs))
    }
}

面试题

let obj = {
  a: 1,
  func() {
    console.log(this.a);
  },
};

let obj2 = { a: 2 };
obj.func.bind(obj2).bind(obj)() // 2

ECMAScript 5 引入了 Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数。但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。

也就是说多次调用bind,this指向最终的结果都是第一次调用bind时传入的参数。这就是为什么会输出2的原因。

使用

我们经常会看到在第三方库中大量使用,在日常搬砖的过程中可能并不常用,但还是有一些使用的地方可以注意

  • 数组合并
let arr1 = [1,2,3]
let arr3 = [4,5,6]
[].push.apply(arr1, arr3) // [1,2,3,4,5,6]
  • 防抖
function debounce (fn, delay = 500) {
  // timer 写在闭包中,因此防抖也是闭包的一个应用
  let timer = null
  function f () {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments)
      timer = null
    }, delay)
  }
  return f
}
  • 继承
function Person (name) {
    this.name = name
}
function Man (name) {
    this.age = 18
    Person.call(this,name)
}
let boy = new Man('柯南')
conosle.log(boy.name, boy.age) // '柯南', 18

结尾

写的不好,还望各位大佬多多指教,感恩家人🙏