1. 什么是call和apply

传送门:了解与应用js中的apply()和call()
至于为什么要手写代码,不仅仅是为了面试,也是为了帮忙咱们更好的了解代码逻辑,深刻其中原理最好的办法。

2. 手写call

写个call办法,跟写一个我的项目是一样的。都要先剖析其需要的性能点,需要剖析要到位,再去一一击破。
首先,咱们须要晓得call是函数原型上的一个办法,它的作用是绑定对象和参数,并执行函数。应用办法如下:

function.call(thisObj, arg1, arg2, ...)

开始实现:

  1. 咱们就给咱们手写的call办法叫myCall,承受的第一个参数 thisObj 是指标函数执行时的指标对象的值,从第二个可选参数arg1开始的其余参数,将作为指标函数执行时的实参。
  2. 须要剖析call函数都有哪些‘需要’

    • 须要判断是否严格模式(this作用域的指向问题,非严格模式须要对thisObj非凡解决)
    • 如何判断严格模式
    • 如果thisObj不是对象类型怎么解决,因为咱们是劫持绑定对象

代码如下:

// 首先apply是原型链Function.prototype上的一个办法Function.prototype.myCall = function() {  // 通过arugments拿到所有参数  // 第一个参数是绑定的this对象  var thisObj = arguments[0];  // 判断是否严格模式  var isStrictMode = (function(){return this === undefined}())  if (!isStrictMode) {    // 如果在非严格模式下,thisObj的值是null或undefined,须要将thisObj置为全局对象    if (thisObj === null || thisObj === undefined) {      // 获取全局对象时兼顾浏览器环境和Node环境      thisObj = (function(){return this}())    } else {      // 非对象类型,须要转换类型      var tthisObjType = typeof thisObj      if (thisObjType === 'number') {       thisObj = new Number(thisObj)      } else if (thisObjType === 'string') {        thisObj = new String(thisObj)      } else if (thisObjType === 'boolean') {        thisObj = new Boolean(thisObj)      }    }  }  // 从索引1开始的残余参数  var invokeParams = [...arguments].slice(1);  // 接下来要调用指标函数,那么如何获取到指标函数呢?  // 实际上this就是指标函数,因为myCall是作为一个办法被调用的,this当然指向调用对象,而这个对象就是指标函数  // 这里做这么一个赋值过程,是为了让语义更清晰一点  var invokeFunc = this;  // 此时如果thisObj对象依然是null或undefined,那么阐明是在严格模式下,并且没有指定第一个参数或者第一个参数的值自身就是null或undefined,此时将指标函数当成一般函数执行并返回其后果即可  if (thisObj === null || thisObj === undefined) {    return invokeFunc(...invokeParams)  }  // 否则,让指标函数成为thisObj对象的成员办法,而后调用它  // 直观上来看,能够间接把指标函数赋值给对象属性,为了key值惟一,避免笼罩掉thisObj对象的原有属性,能够创立一个惟一的属性名,应用Symbol解决  var symbolPropName = Symbol(thisObj)  thisObj[symbolPropName] = invokeFunc  // 返回指标函数执行的后果  return thisObj[symbolPropName](...invokeParams)}

上代码测试一下:

Math.max.myCall(null, 1,2,3,4,5)// 5function mycallTest(a, b) {  var args = [].slice.myCall(arguments)  console.log(arguments, args)}mycallTest(1, 2)var obj = {  name: 'jack'};var name = 'ross';function getName() {  return this.name;}getName();getName.myCall(obj);

能够看到,成果是有的。然而毕竟是手写的,跟源码还是有区别的,思考状况可能会有不到位的中央,会有bug场景。

3. 手写apply

当咱们写了call之后,apply就不便许多了,很多逻辑基本一致。咱们只须要留神两个办法的差别点:第二个参数为数组,上面就不写正文了,间接上代码:

Function.prototype.myApply = function(thisObj, params) {  var isStrict = (function(){return this === undefined}())  if (!isStrict) {    var thisObjType = typeof thisObj    if (thisObjType === 'number') {     thisObj = new Number(thisObj)    } else if (thisObjType === 'string') {      thisObj = new String(thisObj)    } else if (thisObjType === 'boolean') {      thisObj = new Boolean(thisObj)    }  }  var invokeFunc = this;  // 解决第二个参数  var invokeParams = Array.isArray(params) ? params : [];  if (thisObj === null || thisObj === undefined) {    return invokeFunc(...invokeParams)  }  var symbolPropName = Symbol(thisObj)  thisObj[symbolPropName] = invokeFunc  return thisObj[symbolPropName](...invokeParams)}

简略测试一下:

Math.max.myApply(null, [1, 2, 4, 8])// 8

OK,功败垂成!