有冷饭才能炒冷饭
炒冷饭系列第一篇;
今天的冷饭猪脚就是我们的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参数的处理:
- 如果thisArg是对象 则当Fun函数回调时候传递过去用作this指向
- 如果thisArt是原始数据类型,则先转化为对象类型,这是什么意思,举例:1 ⇒ new Number(1)
- 如果thisArg是null 或者undefined,则忽略,
- 当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函数
今天的冷饭就炒到这里,下次在炒其他的 😁