1. call
call方法的作用
call方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数
call方法第一个参数为指定的this值,剩余的参数作为函数的参数传递给函数进行调用.call方法是为改变函数的执行上下文.
- 由于call方法可以被所有方法调用, 所以方法得定义在Function的原型上
- 绑定函数被调用时只传入第二个参数及以后的参数
- 将调用函数绑定到指定的上下文的方法上, 那么在该函数在调用时,内部的this指向该对象
明白了上面的思路我们就可以手写call了
下面是es5 实现方法
Function.prototype.Call = function (content) {
content = content || window;// 给执行上下文 添加默认值
content.fn = this//给 content 添加一个方法指向this;
var args = [];//保存参数集合
for (let i = 1; i < arguments.length; i++) {
args.push(arguments[i])
}
var res = args.length > 1 ? eval('content.fn(' + args + ')') : content.fn() ;//执行fn;
delete content.fn;// 删除fn方法
return res
}
下面是Es6的代码实现
Function.prototype.Call = function(content=window){// 给执行上下文 添加默认值
content.fn = this//给 content 添加一个方法指向this;显示绑定this
// 获取第一个参数以后的所有参数
const args = Array.from(arguments).slice(1);
// 调用上下文的fn属性, 此时函数的this指向上下文, 也就是我们传入的content
const res = arguments.length > 1 ? content.fn(...args) : content.fn();
// 删除上下文的content的fn方法, 避免污染上下文
delete content.fn;
return res
}
2. apply
apply方法和call方法类似,都是Function原型上的方法, 每一个函数作为Function的实例都能调用这两个方法, 都是为了改变调用函数的执行上下文this指向.
区别在于: call传参的时候接受的是一个参数列表,而apply方法的要传的参数接受的是一个参数列表数组, apply实现了思路和call基本相同, 我们只需要对参数进行不同的处理即可.
因为原生的apply第二个参数接受的是一个数组或者类数组对象, 所以我们得对第二个参数类型进行判断
下面是es6的实现代码
Function.prototype._Apply = function (content = window, args = []) {//给content和args添加默认值
if (!(args instanceof Object)) {// 如果第二个参数不是对象的实例,就返回一个错误
throw new TypeError('Create List From Array Like called on non-object');
}
// 显示绑定函数this
content.fn = this;
// 执行fn方法,并接受返回值
const res = arguments[1] ? content.fn(...arguments[1]) : content.fn()
delete content.fn;// 删除fn方法
return res
}
3. bind
bind()
方法创建一个新函数, 在bind()被调用时, 这个新函数的this被指定为 bind()的第一个参数, 而其余参数将作为新函数的参数,供调用时使用.
下面是bind的es6的简易实现
Function.prototype._bind = function(content=window){
const slice = Array.prototype.slice;
// 保存调用函数的this, 也就是原函数
const thatFunc = this;
// 获取第一个参数之后剩余的参数数组
const args = [].slice.call(arguments,1);
return function(){
// 合并返回的新函数的参数
const funcArgs = args.concat(slice.call(arguments))
// 返回新函数调用的结果
return thatFunc.call(content,...funcArgs)
}
}
上面的代码只是对bind的一个简易实现, 没有考虑new运算符的实现,之后会更新对new运算符的实现.