apply、call、bind的区别与精简实现

apply() 方法接收一个指定的this值和一个包含多个参数的数组来调用一个函数。javascript

call() 方法接收一个指定的 this 值和一个参数列表来调用一个函数。前端

bind() 方法建立一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其他参数将做为新函数的参数,供调用时使用java

使用 callapply 函数的时候要注意,若是传递给 this 的值不是一个对象,JavaScript 会尝试使用内部 ToObject 操做将其转换为对象。所以,若是传递的值是一个原始值好比 7'foo',那么就会使用相关构造函数将它转换为对象,因此原始值 7 会被转换为对象,像 new Number(7) 这样,而字符串 'foo' 转化成 new String('foo') 这样,例如:数组

function bar() {
  console.log(Object.prototype.toString.call(this));
}

//原始值 7 被隐式转换为对象
bar.call(7); // [object Number]
bar.call('foo'); // [object String]
复制代码

apply

func.apply(thisArg, [argsArray])闭包

  • thisArg必选的。在 func 函数运行时使用的 this 值。若是这个函数处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,原始值会被包装。
  • argsArray 可选的。一个数组或者类数组对象,其中的数组元素将做为单独的参数传给 func 函数。
Function.prototype.Apply = function (thisArg, args = Symbol.for('args')) {
  //Apply 函数老是被咱们想改变this的函数调用,所以本函数内this老是指代调用函数
  
  //生成一个Symbol类型的惟一符号,用于将调用apply的函数挂载到指定对象上
  const fn = Symbol('fn')      
  thisArg[fn] = this  

  //经过对象调用函数,并传参
  args === Symbol.for('args') ? thisArg[fn]() : thisArg[fn](...args)

  //删除挂载到指定对象上的方法
  delete thisArg[fn]           
}

// 声明全局变量
var position = 'global'
var name = 'window'

function func(name) {
  console.log(this.position)
  console.log(name) 
}

const obj = {
  name: 'object',
  position: 'obj',
}

func.Apply(obj,[obj.name,null]) // obj object
// 其中,Apply内this指向func

func.Apply(obj,[name,null]) // obj window
// func中 name 只受传参影响
复制代码

Symbol.for(key) 会根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol,若是找到了,则返回它,不然,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。app

call

Function.prototype.Call = function (thisArg, ...args) {
  //这里this为这个方法的调用者
  const fn = Symbol('fn')
  thisArg[fn] = this || globalThis

  //经过对象调用函数,并传参
  args.length ? thisArg[fn](...args) : thisArg[fn]() 

  //删除挂载到指定对象上的方法
  delete thisArg[fn]      
}

func.Call(obj)  // obj undefined
func.Call(obj, 'test') // obj test
复制代码

这里不使用arguments对象获取传入参数。arguments不是一个 Array ,除了length属性和索引元素以外没有任何Array属性。函数

使用剩余参数...args能够精简代码post

bind

bind() 函数会建立一个新的绑定函数,它包装了原函数对象。调用绑定函数一般会致使执行包装函数。ui

简单版本this

Function.prototype.Bind = function(thisArg, ...args){
  let self = this;
  let fBound = function(...args1){
    return self.apply(thisArg, [...args, ...args1]);
  }
  return fBound;
}
复制代码

使用闭包保存了第一次绑定时的this值,同时使后续的绑定无效

Function.prototype.Bind = function(thisArg, ...args){
  let self = this;
  
  let fBound = function(...args1){
    //若是当前this为fBound的实例,表示是执行了new,指向this,不然指向bind对象
    return self.apply(this instanceof fBound ? this : thisArg, [...args, ...args1]);
  }
  //修改返回函数的 prototype 为绑定函数的 prototype,new出实例对象就能够继承绑定函数的原型中的成员
  fBound.prototype = this.prototype;
  return fBound;
}
复制代码

这里补充了绑定的函数为构造函数时的状况

function foo(age, height) {
  console.log(this.name)       // obj
  console.log(age)             // 3
  console.log(height)          // 2
}
const obj = {
  name: 'obj',
  age: 3
}
foo.Bind(obj, obj.age)(2) // 绑定bind时同时传递一个参数
复制代码

相关文章:
从零开始的前端筑基之旅(超级精细,持续更新~)
看完就能搞懂的this指向及箭头函数的讲解~

若是你收获了新知识,请给做者点个赞吧~

相关文章
相关标签/搜索