炒冷饭之bind函数

198 阅读3分钟

ys.jpg

有冷饭才能炒冷饭

炒冷饭系列第一篇;

今天的冷饭猪脚就是我们的bind函数。

回忆录

先来回忆一下bind函数作用:创建一个新的函数,并修改函数的this指向。

那么我们很容易得到bind函数的语法公式

Fun = function.bind(thisArg, arg1, arg2, /* …, */ argN)

提出问题

如果这个时候我提出下面函数,阁下如何应对:

function test() {
  console.log(this)
}
const fun = test.bind(1)
fun() // this 打印出来是什么?

const fun2 = new fun(); // this 打印出来是什么

如果阁下已经知道答案,那我走。

针对上面的问题,先来了解一下bind函数对thisArg参数的处理:

  1. 如果thisArg是对象 则当Fun函数回调时候传递过去用作this指向
  2. 如果thisArt是原始数据类型,则先转化为对象类型,这是什么意思,举例:1 ⇒ new Number(1)
  3. 如果thisArg是null 或者undefined,则忽略,
  4. 当Fun函数 用作构造函数时候, thisArg 则被忽略, 举例 new Fun()

到这里 就很容易知道答案了。

// 第一个打印 一个Number 对象
// 第二个 打印的就是test函数自身

到这里,应该知道bind函数如何处理入参 返回的值 是什么。接下来实现一个bind函数 ,那不就是

手到擒来。

实现

先实现一个大体的框架,基于原型实现,这里我将bind函数实现的名称叫做fasten

Function.bind.fasten= function(thisArg, ...rest) {

   const boundFunction= function() {
       
   }
	 return boundFunction
}
// 接受thisArg 和其他参数 
// 返回一个函数

第二步,就是处理thisArg多种数据类型场景

// 获取数据类型
function getDataType(data) {
  return Object.prototype.toString.call(data).slice(8, -1).toLowerCase();
}
// 将原始数据类型转成对象
function wrapPrimitiveToObject(data) {
  const type = getDataType(data);
  let temp;
  switch (type) {
    case 'number':
      temp = new Number(data);
      break;
    case 'string':
      temp = new String(data);
      break;
    case 'boolean':
      temp = new Boolean(data);
      break;
    case 'symbol':
      temp = Object(data);
      break;
    case 'object':
      temp = data;
      break;
    default:
      temp = window;
      break
  }
  return temp;
}

Function.bind.fasten= function(thisArg, ...rest) {
	 const ctx = wrapPrimitiveToObject(thisArg)
   const boundFunction= function() {
       
   }
	 return boundFunction
}

第三步 修改执行函数的this指向,js中函数有个特点就是谁调用this就指向谁(箭头除外,主打一个自由)

Function.bind.fasten= function(thisArg, ...rest) {
	 const ctx = wrapPrimitiveToObject(thisArg)
   const F = this; // 获取调用函数
   const prototype = F.prototype; // 为后续当作构造函数 判断使用
   const boundFunction= function() {
       if (!prototype) {
         // 处理箭头函数场景 箭头函数没有prototype
          return F(...rest, ...args);
        }

				//直接调用F 这个是时候 就该修改F函数内部的this指向
		    const fun = Symbol('fun');
		    ctx[fun] = F; // 谁调用 就指向谁 这个地方就是修改了F函数this
		    const res = ctx[fun](...rest, ...args);
		    delete ctx[fun];
		    return res;
   }
	 return boundFunction
}

第四步,处理bind函数返回的值被用作构造函数场景

Function.prototype.fasten = function (thisArg, ...rest) {
  const ctx = wrapPrimitiveToObject(thisArg);
  const F = this; // 获取调用函数
  const prototype = F.prototype; // 为后续当作构造函数 判断使用
  const boundFunction = function (...args) {
    if (!prototype) {
      // 处理箭头函数场景
      return F(...rest, ...args);
    }
    // 如果boundFunction 被当作构造函数使用 那么等于直接 new F函数
    if (this instanceof boundFunction) {
      return new F(...rest, ...args);
    }
    //直接调用F 这个是时候 就该修改F函数内部的this指向
    const fun = Symbol('fun');
    ctx[fun] = F; // 谁调用 就指向谁 这个地方就是修改了F函数的this指向
    const res = ctx[fun](...rest, ...args);
    delete ctx[fun];
    return res;
  };
  if (prototype) {
    boundFunction.prototype = prototype;
  }
  return boundFunction;
};

到这里 一个bind函数基本已经完成了。手撸的核心是理解bind函数

今天的冷饭就炒到这里,下次在炒其他的 😁

参考

完整代码

箭头函数

core-js

博客园