1. call模拟实现
借用MDN一句话: call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
//举个例子eg:
var a = "window";
var obj = {a: "obj"};
function getA(b){
console.log(this.a)
console.log(b);
}
getA.call(null, "2"); //window 2 在严格模式下面 会打印undefined
getA.call(obj, 2); //obj 2
可以看出:
1. 函数getA中this的指向变了
2. 函数getA自动执行了
3. 第二个参数之后是传入的参数
//代码实现
Function.prototype.myCall = function(context){
context = context || window;
context.fn = this;
var args = []; //call 第二个参数之后是传入的值
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')'); //使用eval执行函数
delete context.fn;
return result;
}
备注:eval 函数可计算某个字符串,并执行其中的的 JavaScript 代码。eval 函数如果传参是数组, 则会把数组转换成字符串
var a = [1,2,3];
eval("console.log(" + a + ")") ;// 1 2 3
//es6 实现
Function.prototype.myCall2 = function(context){
context = context || window;
context.fn = this;
var args = Array.from(arguments).slice(1, arguments.length); //call 第二个参数之后是传入的值
context.fn(...args);
delete context.fn;
return result;
}
2. apply模拟实现
apply与call实现方式类似,唯一区别就是参数不同,apply接受数组
Function.prototype.myApply = function(context, arr){
context = context || window;
context.fn = this;
var result ;
if(!arr){
result = context.fn();
}else{
result = context.fn(arr)
}
delete context.fn;
return result;
}
3. bind模拟实现
bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(MDN)
Function.prototype.myBind = function(context){
if(typeof this !== "function"){
return new Error("xxx");
}
var self = this;
//缓存bind时候传入的参数
var args = Array.prototype.slice.call(arguments, 1);
var F = function(){};
var fBound = function () {
//获取新的参数
var bindArgs = Array.prototype.slice.call(arguments);
//判断是否是new,如果是new 则返回这个实例
return self.apply(this instanceof F ? this : context, args.concat(bindArgs));
}
//使用架桥函数 过滤掉原来函数this上面的值
F.prototype = this.prototype;
fBound.prototype = new F();
return fBound;
}