阅读 717

实现call、apply和bind

callapplybind有一个共同作用,绑定函数的this,对于this的绑定,小黄书(《你不知道的JavaScript》)介绍了4种绑定方式。

  1. 默认绑定
  2. 隐式绑定
  3. 显示绑定
  4. new绑定

这3个函数就属于第3种情况。

先来看看调用原生call时第一个参数不是对象的时候 this的绑定情况

function test(){
    console.log('this',this)
}
test.call() //this Window
test.call(undefined) //this Window
test.call(null) //this Window
test.call(123) //this Number{123}
test.call('abc') //this String{"abc"}
test.call(true) //this Boolean{true}
复制代码

可以看出,第一个参数传undefinednull时,this指向全局对象,浏览器环境下为window对象。第一个参数传其他基本类型(原始类型)时会自动装箱使其变为对象。这一点很重要。

call、apply

为将基本类型转为对应的引用类型,需要利用到它的构造函数

const a = 1;
const aObj = new a.constructor(a);
console.log(aObj); // Number{1}
复制代码

其他基本类型也是如此。

call函数实现如下。

Function.prototype.myCall = function(context, ...args){
    if(typeof this !== 'function'){
    	throw Error('caller must be a function!')
    }
    //待绑定的this上下文
    let _context = context;
    //传入this为undefined或null,待绑定的this上下文指向全局对象
    if(_context === undefined || _context === null){
    	_context = globalThis;
    }
    //传入this为基本类型时,将其转为对应的引用类型
    if(typeof _context !== 'object'){
      _context = new _context.constructor(context)
    }
    _context.func = this;
    //调用函数并绑定this
    const res = _context.func(...args);
    delete _context.func;
    //返回执行结果
    return res;	
}
复制代码

applycall很相似,区别在于参数传递方式

func.call(obj,arg1,agr2,...);
func.apply(obj,[arg1,arg2,...])
复制代码

apply函数实现如下。

Function.prototype.myApply = function(context, args){
    if(typeof this !== 'function'){
    	throw Error('caller must be a function!')
    }
    //待绑定的this上下文
    let _context = context;
    //传入this为undefined或null,待绑定的this上下文指向全局对象
    if(_context === undefined || _context === null){
    	_context = globalThis;
    }
    //传入this为基本类型时,将其转为对应的引用类型
    if(typeof _context !== 'object'){
      _context = new _context.constructor(context)
    }
    let _args = args;
    if(_args === undefined || _args === null){
        _args = []
    } else if(!Array.isArray(args)){
        throw new TypeError("CreateListFromArrayLike called on non-object")
    }
    _context.func = this;
    //调用函数并绑定this
    const res = _context.func(..._args);
    delete _context.func;
    //返回执行结果
    return res;	
}
复制代码

bind

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

Function.prototype.myBind = function (context,...args) {
  if (typeof this !== 'function') {
      throw new TypeError('caller must be a function!');
  }
  
  //待绑定的函数
  let toBindFunc = this
  // 返回一个函数
  return function boundFunction() {
    // 因为返回了一个函数,可能会通过new调用
    if (this instanceof boundFunction) {
      //传入的参数覆盖默认参数
      return new toBindFunc(...args, ...arguments)
    }
    //传入的参数覆盖默认参数
    return toBindFunc.apply(_context, args.concat(...arguments))
  }
}
复制代码
文章分类
前端
文章标签