call函数
原理解析:
(1)利用原型链,在Function上定义callFun函数,函数调用时通过原型链在Function的prototype上找到callFun方法。
(2)利用this指向调用者,由于this指向调用者,当mbs.say.callFun调用时,say为调用者,此时call中的this指向say函数,通过给传入对象target新增对象属性,为对象添加say函数;执行时由于调用者是target对象,此时say函数中的this指向了target,从而实现改变this指向的功能。
(3)执行完成后,删除新增的属性。
实现:
Function.prototype.callFun = function (target, ...args) {
const sKey = Symbol()
target = target || window
target[sKey] = this
const result = target[sKey](...args)
delete target[sKey]
return result
}
const obj = {
base: 'MT',
keep(args) {
console.log(`args : ${args}, base : ${this.base}`)
}
}
const A = {
base: 'XBLL'
}
obj.keep(3);
obj.keep.callFun(A, 9);
apply函数
原理解析:
同call函数,只是把参数换成了数组,其他都一样;
实现:
Function.prototype.applyFun = function (target, args) {
const sKey = Symbol()
target = target || window
target[sKey] = this
const result = target[sKey](...args)
delete target[sKey]
return result;
}
const obj = {
base: 'MT',
keep(args) {
console.log(`args : ${args}, ${this.base}`)
}
}
const A = {
base: 'XBLL'
}
obj.keep(9);
obj.keep.applyFun(A, [9]);
bind函数
原理解析:
(1)按照已经实现的call和aplly方法,bind的原理是一样的。首先通过在Function的prototype中添加属性bindFun。然后利用this指向调用者的特性将方法转移到目标对象上,再利用目标对象执行函数,此时由于this指向的是执行者,从而将调用函数中的this指向目标对象,完成this的指向转移。
(2)但是bind返回的并不是执行结果,而是一个函数。如果直接把函数作为返回值。 例如:
Function.prototype.bindFun = function(target) {
const sKey = Symbol()
target = target || {}
target[sKey] = this
return target[sKey];
}
const obj = {
base: 'MT',
keep() {
console.log(`base : ${this.base}`);
}
}
const B = {
base: 'XBLL'
}
obj.keep()
const keepB = obj.keep.bindFun(B);
keepB();
结果是并没有拿到B中的name。
因为此时函数内部this指向的是全局的window对象,可以在window上挂载一个base属性,会发现这时会获取到window对象上的name(因为此时没有像之前call那样通过target对象直接去执行,this指向的是调用者,所以单纯执行函数时this就指向了全局的windowd对象)。
(3)这时我们可以利用闭包的特性,返回函数时将target对象捕获,然后再将函数返回。例如:
Function.prototype.bindFun = function(target) {
const sKey = Symbol();
target = target || {};
target[sKey] = this;
return function() {
target[sKey]();
}
}
完整的实现:
Function.prototype.bindFun = function (target, ...param1) {
const sKey = Symbol()
target = target || {}
target[sKey] = this
return function (...param2) {
target[sKey](...param1, ...param2);
}
}
const obj = {
base: 'MT',
keep(args1, args2) {
console.log(`args1 : ${args1}, args2: ${args2}, base: ${this.base}`)
}
}
const B = {
base: 'XBLL',
}
obj.keep('hello', 6);
const keepB = obj.keep.bindFun(B, 'hello');
keepB(9);