call的实现
Function.prototype.myCall = function () {
let [context,...args] = [...arguments]
context = context || window // 如果不传context,非严格模式下浏览器环境会指向window
context.fn = this // 借用this对应的方法
let result = context.fn(...args)
delete context.fn // 调用该方法后清除该上下文环境上的方法
return result
}
apply的实现
apply其实与call的实现差不多的,只是apply的第二个参数为一个数组
Function.prototype.myApply = function (context){
let args = arguments[1]
context.fn = this
let result
if(args){
result = context.fn(...args)
}else{
result = context.fn()
}
delete context.fn
return result
}
bind实现
call、apply是直接返回的改变上下文函数执行的结果,bind是返回绑定了上下文的函数
Function.prototype.myBind = function (){
const that = this
let [context,...rest] = [...arguments]
return function Fn(...args){ //考虑new的情况。MDN解释:当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。不过提供的参数列表仍然会插入到构造函数调用时的参数列表之前。
if(this instanceof Fn) {
return new that(...rest,...args)
}else{
that.apply(context,[...rest,...args])
}
}
}
如果只是实现bind的基本功能,可以简写为一行代码:
const bind = (fn, context , ...rest) => (...args) => fn.apply(context, [...rest, ...args])
// 写法参考30secondsofcode
MDN bind方法的polyfill实现:
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
// this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
return fToBind.apply(this instanceof fBound
? this
: oThis,
// 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// 维护原型关系
if (this.prototype) {
// 当执行Function.prototype.bind()时, this为Function.prototype
// this.prototype(即Function.prototype.prototype)为undefined
fNOP.prototype = this.prototype;
}
// 下行的代码使fBound.prototype是fNOP的实例,因此
// 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
fBound.prototype = new fNOP();
return fBound;
};
}