1.apply
- 判断this是否传入,没有则指向全局
- 通过将调用的函数绑定在传入的context上的方式,context.fn()调用,将fn的this指向context
- 判断参数数组是否传入,没有则直接调用得到结果,反之使用eval函数结算结果
- 最终删除context上的fn属性,恢复context的初始状态,将结果返回
Function.prototype.apply = function(context, arr){
context = context || (typeof window === 'undefined') ? global : window;
let res;
context.fn = this;
if(!arr || arr.length === 0){
res = context.fn();
}else{
let args = [];
for(let i = 0; i < arr.length; i++){
args.push("arr[" + i + "]");
}
res = eval("context.fn(" + args + ")");
// res = context.fn(...arr); ES6
}
delete context.fn;
return res;
}
2.call
- 判断this是否传入,没有则指向全局
- 通过将调用的函数绑定在传入的context上的方式,context.fn()调用,将fn的this指向context
- 使用eval函数结算结果
- 最终删除context上的fn属性,恢复context的初始状态,将结果返回
Function.prototype.call = function(context){
context = context || (typeof window === 'undefined') ? global : window;
let res;
context.fn = this;
let args = [];
for(let i = 1; i < arguments.length; i++){
args.push("arguments[" + i + "]");
}
res = eval("context.fn(" + args + ")");
delete context.fn;
return res;
}
3.bind
- 取出this的引用存入变量中
- 创建一个干净的函数用于新函数的原型继承
- bind中传入的参数会插入新函数调用时的实参的前面
- bind返回的新函数支持new调用,判断是否是new调用来决定this的指向
- 通过圣杯模式采用的原型继承方式继承原型对象
Function.prototype.bind = function(obj){
let fn = this;
// 干净的函数
let _fn = function(){};
let args = Array.prototype.slice.call(arguments, 1);
function bound(){
// 支持柯里化
let params = Array.prototype.slice.call(arguments);
// 支持new调用,判断是否是new调用来决定this指向
return fn.apply((this instanceof fn_) ? this : obj || window, args.concat(params));
}
// 圣杯模式的原型对象继承方式
fn_.prototype = fn.prototype;
bound.prototype = new fn_();
return bound;
}
手撕代码系列