前言
不能使用ES6的语法糖来实现,比如(解构赋值,symbol)
call方法
想要对call 的重写,前提是需要知道 call 的几个特点:(假设有一个 函数 test)
- test 调用 call() ,那么就会执行 text。
- test 中的this 指向 call 的第一个参数。
- call 的第二个参数开始,都在test函数的参数列表中
function test() {
console.log(this,arguments);
}
test.call({a:1,b:2},'张三','李四')
重写call
在重写call前,我需要知道一个知识:
谁调用这个函数,这个函数内的this就指向谁
上述的 test 函数,默认是window调用它的,所以 test函数的this就指向 window
代码实现
Function.prototype.myCall = function (context) {
context = context ? Object(context) : window; // 简化了, 这里就直接把数据类型都转换为Object了
let uniqueKey = new Date().getTime().toString(); // 创建一个唯一值,避免方法名冲突
context[uniqueKey] = this;
let args = [];
for (let i = 1; i < arguments.length; i++) {
args.push('arguments[' + i + ']');
}
const result = eval('context[uniqueKey](' + args + ')');
delete context[uniqueKey];
return result;
};
重写 apply
它和 call 的区别就在于第二个参数是数组.
但还是需要注意一下:
- apply 的 第二个参数接收 数组
- apply 只取到第二个参数,从第三个参数开始到最后直接被忽略
- apply 的 第二个参数,如果是 object/funciton/null/undefined ,那么 arguments.length 直接为 0
- apply 的 第二个参数, 如果是 number/string/boolean,会报错
代码实现
Function.prototype.myApply = function (context, args) {
context = context ? Object(context) : window;
let uniqueKey = new Date().getTime().toString();
context[uniqueKey] = this;
if (typeof args === 'number' || typeof args === 'string' || typeof args === 'boolean') {
throw new TypeError('CreateListFromArrayLike called on non-object');
}
if (!args || Array.isArray(args) === false) {
return context[uniqueKey]();
}
// 代码到这里了就证明args是一个数组了,
let result = context[uniqueKey](...args);
delete context[uniqueKey];
return result;
};
重写bind
写之前,先看一看bind都有哪些特点:(假设有一个 函数 test)
- 使用 bind() 时,test 不会执行,而是返回一个新的函数
- bind 的第一个参数和 call,apply 一样的特点
- bind 可以分离 text 的参数 (函数柯里化)
- bind 可以接收一部分参数,返回的新函数也可以接收一部分参数
- 返回的新函数可以作为构造函数,而this指向的是 test 构造出来的实例
- 实例应该继承test构造函数上的原型属性
根据以上特点来实现一下
代码实现
Function.prototype.myBind = function (context) {
let _this = this,
// bind()传的参数列表
args = [].slice.call(arguments, 1), // 去掉第一个,因为第一个是this指向的东西
_tempFn = function () {}; // 作为原型链继承的中间件函数
let newFn = function () {
// 返回的新函数的参数列表
let newArgs = [].slice.call(arguments);
return _this.apply(this instanceof newFn ? this : context, args.concat(newArgs));
};
// newFn.prototype = this.prototype // 这个共用了一个原型,不太好
_tempFn.prototype = this.prototype;
newFn.prototype = new _tempFn();
return newFn;
};