Fe-interview: [js] 第26天 说说bind、call、apply的区别?并手写实现一个bind的方法

Created on 11 May 2019  ·  15Comments  ·  Source: haizlin/fe-interview

第26天 说说bind、call、apply的区别?并手写实现一个bind的方法

js

Most helpful comment

callapply都是为了解决改变this的指向。作用都是相同的,只是传参的方式不同。

除了第一个参数外,call可以接收一个参数列表,apply只接受一个参数数组。 bind绑定完之后返回一个新的函数,不执行。

Function.prototype.myCall = function (context = window) {
  context.fn = this;

  var args = [...arguments].slice(1);

  var result = context.fn(...args);
  // 执行完后干掉
  delete context.fn;
  return result;
}
Function.prototype.myApply = function (context = window) {
  context.fn = this;

  var result
  // 判断 arguments[1] 是不是 undefined
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }

  delete context.fn
  return result;
Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

All 15 comments

callapply都是为了解决改变this的指向。作用都是相同的,只是传参的方式不同。

除了第一个参数外,call可以接收一个参数列表,apply只接受一个参数数组。 bind绑定完之后返回一个新的函数,不执行。

Function.prototype.myCall = function (context = window) {
  context.fn = this;

  var args = [...arguments].slice(1);

  var result = context.fn(...args);
  // 执行完后干掉
  delete context.fn;
  return result;
}
Function.prototype.myApply = function (context = window) {
  context.fn = this;

  var result
  // 判断 arguments[1] 是不是 undefined
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }

  delete context.fn
  return result;
Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  var _this = this
  var args = [...arguments].slice(1)
  // 返回一个函数
  return function F() {
    // 因为返回了一个函数,我们可以 new F(),所以需要判断
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    }
    return _this.apply(context, args.concat(...arguments))
  }
}

上面同学说的太好了,简洁好记!!忍不住重复一遍。

call() 和 apply() 作用都是改变 this 的指向,区别是传参的方式不同。除了第一个参数外,call() 可以接收一个参数列表,apply() 只接受一个参数数组。 bind() 绑定完之后返回一个新的函数,不执行。

  • bind
    bind(context,arguments) 返回一个函数
  • call
    call(context,arg1,arg2...) 指定作用域 同时执行函数
  • apply
    apply(context,args) 指定作用域 同时执行函数,后面的参数是数组

实现bind

Function.prototype.myBind(context,...args){
 return function(){
   return this.apply(context,args)
 }
}

1:call与apply基本一样,唯一不一样的是接收的参数

2:bind则是改变this后返回一个新函数

function bindFun(cb, ctx) {
        // 一些限制没加
    return (...args) => cb.call(ctx || this, args);
}
bindFun(b, a)();
Function.prototype.customBind = function (ctx, ...args) {
  return (...args2) => this.apply(ctx, args.concat(args2));
};

bind, call, apply 三者都可以改变 this 的指向。

其中 callapply 为立即执行,两者效果等价,只有在传參形式上有所区别。call 需要把参数一个一个传入 fun.call(obj, arg1, arg2, arg3,...)apply 接受一个数组作为参数 fun.apply(obj, [arg1, arg2, arg3, ...])

bind 则是延时执行。const fb = fun.bind(obj) fb(arg1, arg2, ...) 在使用 bind 之后,只会返回一个修改了作用域的函数,等再次调用时才会执行。

function customBind(fun, obj) {
  return (...args) => {
    fun.apply(obj, [...args]);
  };
}
  Function.prototype.call1 = function(fn, ...args) {
    const symbol = Symbol()
    fn = fn || window
    fn[symbol] = this
    let result = fn[symbol](...args)
    delete fn[symbol]
    return result
  }
  Function.prototype.apply1 = function(fn, arr = []) {
    // const symbol = Symbol()
    // fn = fn || window
    // fn[symbol] = this
    // let result = fn[symbol](...arr)
    // delete fn[symbol]
    // return result
    return this.call1(fn, ...arr)
  }



md5-72d44af7d304a44d682201b6e5f98e70



  Function.prototype.bind1 = function(fn, ...args) {
    const _this = this
    const bound = function(...arguments) {
      return _this.apply1(this instanceof bound ? this : fn, [...args, ...arguments])
    }
    bound.prototype = Object.create(_this.prototype || null)
    return bound
  }

https://www.runoob.com/w3cnote/js-call-apply-bind.html
这个帖子对于三者区别写的很详细

bind

bind() 方法不会调用函数,他会绑定其他元素,当其他元素触发事件时改变 this 的指向

语法:fun.bind(thisArg, arg1, arg2, ...)
参数:

  • thisArg:在 fun 函数运行时指定的 this 值
  • arg1,arg2:传递的其他参数
  • 可以传参数,但是不会调用,可以赋值给其他元素

注意:

  • 返回值改变 this 的指向
  • 他不会调用函数,因此在不需要立即执行的事件上很好用

实现 bind 方法

Function.prototype._bind = function() {
  if (typeof this !== 'function') {
    throw new Error(
      'Function.prototype.bind - what is trying to be bound is not callable'
    )
  }
  let self = this
  // 需要传入self的参数
  let args = Array.prototype.slice.call(arguments, 1)
  return function() {
    return self.apply(self, args)
  }
}
Function.prototype._bind = Function.prototype._bind || function() {}

call() 方法

call() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向

语法:fun.call(thisArg, arg1, arg2, ...)
参数:

  • thisArg:在 fun 函数运行时指定的 this 值
  • arg1,arg2:传递的其他参数

注意:

  • 返回值就是函数的返回值,因为它就是调用函数
  • 因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承
  • call()无参数 / call(null) / call(undefined);这三种 this 都指向 window

实现 call 方法

Function.prototype._call = function() {
  if (typeof this !== 'function') {
    throw new Error(
      'Function.prototype.bind - what is trying to be bound is not callable'
    )
  }
  let self = this
  let pointTo = arguments[0] || window
  // 需要传入self的参数
  let args = Array.from(arguments).slice(1)
  pointTo.fn = self
  pointTo.fn(...args)
  // 需要传入self的参数
}
Function.prototype._call = Function.prototype._call || function() {}

apply 方法

apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向,参数为数组形式

语法:fun.apply(thisArg, [arg1, arg2, ...])
参数:

  • thisArg:在 fun 函数运行时指定的 this 值
  • arg1,arg2:传递的其他参数放在中括号内

注意:

  • 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
  • 和 call()的区别就是,apply()的参数放在中括号内
    实现 call 方法
Function.prototype._apply = function() {
  if (typeof this !== 'function') {
    throw new Error(
      'Function.prototype.bind - what is trying to be bound is not callable'
    )
  }
  let self = this
  let pointTo = arguments[0] || window
  // 需要传入self的参数
  let [args] = Array.from(arguments).slice(1)
  pointTo.fn = self
  pointTo.fn(...args)
  // 需要传入self的参数
}
Function.prototype._apply = Function.prototype._apply || function() {}

有错请指出,感谢

来和我一起刷3+1吧
gitHub: https://github.com/Renato-Z/exercises/blob/master/history.md

// call
Function.prototype._call = function (target=window, ...res) {
    target._fun = this;
    const ret = target._fun(...res);
    delete target._fun;
    return ret;
}
// apply
Function.prototype._apply = function (target=window, list=[]) {
    target._fun = this;
    const ret = target._fun(...list);
    delete target._fun;
    return ret;
}
// bind
Function.prototype._bind = function (target, ...res) {
    if (typeof this !== 'function') {
      throw new TypeError('Error')
    }
    const that = this;
    const _func = function(...args) {
        // 判断当前韩式是直接访问还是通过new进行构造
        return that.apply(this instanceof _func ? this : target, res.concat(args));
    }
    // 添加一层原型,防止修改_func.prototype时影响到this.prototype
    _func.prototype = Object.create(this.prototype);
    return _func;
}

call、apply和bind作用都是为了改变this的指向
call、apply会立即执行此函数,两者等价,第一个参数一致。apply第二个参数是数据,call是接受一个参数列表
bind是返回一个新的函数。第二个参数也是一个参数列表

function bind(f,o) {
    if (f.bind) return f.bind(o)
    else return function() {
        renturn f.apply(o,arguments);
    }
}

call() 和 apply() 作用都是改变 this 的指向,区别是传参的方式不同。除了第一个参数外,call() 可以接收一个参数列表,apply() 只接受一个参数数组。 bind() 绑定完之后返回一个新的函数,不执行。

Function.prototype.customCall = function (context) {
    context.fn = this;
    let params = Array.from(arguments)
    let result = context.fn(...params.slice(1))
    delete context.fn;
    return result;
}

Function.prototype.customApplay = function (context) {
    context.fn = this;
    let result = context.fn(...arguments[1])
    delete context.fn;
    return result;
}

Function.prototype.customBind = function (context) {
    context.fn = this;
    let params = Array.from(arguments)
    return function F() {
        let result = context.fn(...(params.slice(1).concat(Array.from(arguments))))
        return result;
    }
}
const run = function(x,y){ return x + y + this.z }
const obj = {z: 3}
const res = run.bind(obj, 1, 2)
console.log(res())

Function.prototype.myBind = function(){
  if (typeof this !== 'function') {
    throw new TypeError('not funciton')
  }
  const [context, ...args] = arguments
  const symbolFn = Symbol('fn')
  context.symbolFn = this

  return function() {
    const res = context.symbolFn(...args)
    delete context.symbolFn
    return res
  }
}

console.log(run.myBind(obj, 4, 5)())


Function.prototype.mybind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  let _this = this
  let arg = [...arguments].slice(1)
  return function F() {
    // 处理函数使用new的情况
    if (this instanceof F) {
      console.log(this)
      return new _this(...arg, ...arguments)
    } else {
      console.log(arguments)
      return _this.apply(context, arg.concat(...arguments))
    }
  }
}
Was this page helpful?
0 / 5 - 0 ratings