fe-interview icon indicating copy to clipboard operation
fe-interview copied to clipboard

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

Open haizhilin2013 opened this issue 5 years ago • 20 comments

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

haizhilin2013 avatar May 11 '19 20:05 haizhilin2013

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))
  }
}

Amour1688 avatar May 12 '19 01:05 Amour1688

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

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

AnsonZnl avatar May 12 '19 08:05 AnsonZnl

  • 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)
 }
}

myprelude avatar Jun 13 '19 07:06 myprelude

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

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

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

Damon99999 avatar Jun 20 '19 04:06 Damon99999

Function.prototype.customBind = function (ctx, ...args) {
  return (...args2) => this.apply(ctx, args.concat(args2));
};

Vi-jay avatar Jul 29 '19 09:07 Vi-jay

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]);
  };
}

Konata9 avatar Aug 12 '19 15:08 Konata9

  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)
  }
  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
  }

jiamianmao avatar Aug 14 '19 06:08 jiamianmao

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

BYC04 avatar Sep 20 '19 05:09 BYC04

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

rennzhang avatar Apr 01 '20 08:04 rennzhang

// 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;
}

13168335674 avatar Jun 09 '20 03:06 13168335674

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

blueRoach avatar Jun 22 '20 09:06 blueRoach

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

laboonly avatar Aug 24 '20 15:08 laboonly

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

MrZ2019 avatar Sep 27 '20 08:09 MrZ2019

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;
    }
}

bozaigao avatar Oct 06 '20 01:10 bozaigao

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))
    }
  }
}

m7yue avatar Oct 10 '20 08:10 m7yue

call 改变this的指向,参数只有对象一个 apply 也能改变this的指向,让变量能调用别人的方法,可以传入参数,用数组传入参数

zxcdsaqwe123 avatar Oct 15 '21 17:10 zxcdsaqwe123

改变函数执行时的上下文,再具体一点就是改变函数运行时的this指向。 bind bind方法是事先把fn的this改变为我们要想要的结果,并且把对应的参数值准备好,以后要用到了,直接的执行即可,也就是说bind同样可以改变this的指向,但和apply、call不同就是不会马上的执行 注意:bind这个方法在IE6~8下不兼容。 区别 上面看起来三个函数的作用差不多,干的事几乎是一样的,那为什么要存在3个家伙呢,留一个不就可以。所以其实他们干的事从本质上讲都是一样的动态的改变this上下文,但是多少还是有一些差别的.. call、apply与bind的差别 call和apply改变了函数的this上下文后便执行该函数,而bind则是返回改变了上下文后的一个函数。

call、apply的区别 他们俩之间的差别在于参数的区别,call和apply的第一个参数都是要改变上下文的对象,而call从第二个参数开始以参数列表的形式展现,apply则是把除了改变上下文对象的参数放在一个数组里面作为它的第二个参数。

github-cxtan avatar Feb 19 '22 08:02 github-cxtan

call、apply、bind都是Function.protoype伤的方法,可以改变方法的this指向。 call和apply都是立即调用,区别为call是传入参数列表,apply是传入参数数组。 bind和call、apply的区别是bind会返回一个函数,不会立即调用。 function myCall (_this, ...args) { _this.fn = this; let result = _this.fn(...args); delete _this.fn; return result; } function myApply (_this, args) { _this.fn = this; let result = _this.fn(...args); delete _this.fn; return result; } function myBind (_this, args) { return () => { return myApply(_this, args); } }

yxllovewq avatar Mar 09 '22 06:03 yxllovewq

// call和apply 都是改变上下文的,也就是改变this指向,
// 区别就是apply(this, array)的第二个参数是数组也可以是类数组,但call不是 是(this,arg1,arg2,arg3,...)
// 1)判断this是不是函数
// 2)确定执行上下文为context还是window
// 3)将context.fn作为this,
// 4)获取函数的传参,从第二个参数开始取,因为bind,call,apply的第一个传参为this改变后的对象
// 5)执行该函数,因为已经将this指定给了context.fn所以执行的context.fn
// 6)清除context.fn新设定的属性
// 7)返回result
Function.prototype.myCall = function(context) {
  if (typeof this != 'function') {
    throw new Error('type error')
  }
  context = context || window
  context.fn = this
  //获取函数的传参,从第二个参数开始取,因为bind,call,apply的第一个传参为this改变后的对象
  var args = [...arguments].slice(1)
  // 数组解构
  let result = context.fn(...args)
  delete context.fn
  return result
}

Function.prototype.myApply = function(context) {
  if (typeof this != 'function') {
    throw new Error('type error')
  }
  context = context || window
  context.fn = this
  let result
  // apply 第二项是数组
  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 Error('type error')
  }
  context = context || window
  self = this
  //获取函数的传参,从第二个参数开始取,因为bind,call,apply的第一个传参为this改变后的对象
  var args = [...arguments].slice(1)
  // return function F() {
  //   // 因为返回了⼀个函数,我们可以 new F(),所以需要判断
  //   if (this instanceof F) {
  //     return new self(...args, ...arguments)
  //   }
  //   return self.myApply(context, args.concat(...arguments))
  // }
  return function() {
    return self.myApply(context, args.concat(...arguments))
  }
}

// 示例
function add(a, b, ...args) {
  let result = 0
  if (args) {
    result = args.reduce((pev, cur) => {
      return pev + cur
    }, 0)
  }
  return a + b + this.c + result
}
var c = 100
var newObj = {
  c: 200
}
console.log(add.myApply(newObj, [10, 20]))
console.log(add.myCall(null, 10, 20))
var test = add.myBind(newObj, 10, 20)
console.log(test(600, 200))
// var newTest = add.bind(newObj, 10, 30)
// console.log(newTest(1000))
// function fn(name, age) {
//   console.log(name, age)
// }
// let callObj = {
//   name: 'xq call',
//   age: 'xq 18岁'
// }
// fn.myCall(callObj, callObj.name, callObj.age)
// fn.call(callObj, 'xq', 18)

xiaoqiangz avatar May 29 '22 03:05 xiaoqiangz

call、apply、bind都是用来改变this指向的,区别是,call和apply的参数不一样,第一个参数都是this指向要指向谁,而apply的第二个参数是一个数组,call则是参数列表,它们都是调用后就执行,bind则是调用后返回一个新的函数,bind的参数与call一样;

Function.prototype.ownCall = function(context){
    let context = context || window;  //如果context为null或者undfined则this指向window
    let key = Symbol();    //创建一个独一无二的值,避免覆盖掉context中的属性
    context[key] = this  //这里的this是指调用方法的函数
    let args = [...arguments].slice(1)  //参数
    let result = context[key](...args);
    delete context[key];
    return result;
}
Function.prototype.ownApply = function(context){
    let context = context || window;
    let key = Symbol();
    context[key] = this;
    if(arguments[1]){
        let result = context[key](...arguments[1]);
    }else{
       let result = context[key]();
    }
    delete context[key];

   return result;
}
Function.prototype.ownBind = function(context){
    let _self = this,
        args = [].slice.call(arguments, 1)  //ES5写法
    let fn = function(){
        return _self.apply(this instanceof fn ? this : context, args.concat([].slice,call(arguments)));
    }
    let fn1 = new Function();
    fn1.prototype = _self.prototype;
    fn.prototype = new fn1();
   
   return fn;
}

wyy-g avatar Sep 15 '22 14:09 wyy-g

call和apply,bind函数是改变this指向,call和apply传入参数的方式不同,apply传参是以一个数组形式,call是直接在后面进行传参,call,apply和bind的区别是前者会直接调用函数,后者返回一个函数,函数的this指向已被改为bind传入的新this call实现 `Function.prototype.mycall = function(thisArg,...args){ var fn = this thisArg = thisArg ? Object(thisArg) : window

thisArg.fn = fn var result = thisArg.fn(...args) delete thisArg.fn return result }`

shengjie9 avatar Nov 10 '22 01:11 shengjie9

bindcallapply 都是用于改变函数的执行上下文(即 this 的指向)的方法,但它们的使用方式和效果略有不同。

区别如下:

  1. bind 方法:
  • bind 方法会创建一个新的函数,并将指定的上下文对象绑定到该函数。
  • bind 方法不会立即执行函数,而是返回一个绑定了上下文的新函数。
  • 可以传递额外的参数给 bind 方法,这些参数会作为新函数的预设参数。
  • 绑定后的函数可以被延迟执行,或者作为回调函数传递给其他函数。
  1. call 方法:
  • call 方法立即执行函数,并将指定的上下文对象绑定到该函数。
  • call 方法接受一个上下文对象作为第一个参数,后续参数是传递给函数的参数列表。
  • call 方法会改变函数的执行上下文并立即执行函数,返回函数的执行结果。
  1. apply 方法:
  • apply 方法立即执行函数,并将指定的上下文对象绑定到该函数。
  • apply 方法接受一个上下文对象作为第一个参数,第二个参数是一个数组,包含传递给函数的参数列表。
  • apply 方法会改变函数的执行上下文并立即执行函数,返回函数的执行结果。

下面是一个手写实现的 bind 方法的示例:

function myBind(func, context, ...args) {
  return function(...innerArgs) {
    return func.apply(context, [...args, ...innerArgs]);
  };
}

上面的代码中, myBind 函数接受一个函数 func 、一个上下文对象 context 和任意数量的预设参数 args 。它返回一个新函数,该函数在执行时将绑定了上下文和预设参数,并将传递给新函数的参数与预设参数合并后,通过 apply 方法调用原始函数。

这样,我们就可以使用 myBind 方法来手动实现 bind 的功能。

never123450 avatar Sep 04 '23 12:09 never123450