手撸bind apply call 改变函数的this指向
call.(obj,arg1,arg2,arg3…)
call的用法:
首先传入一个对象,再传入对应的参数进行解构赋值,返回函数结果。
错误情况:call中没有传入参数,则默认obj为window
代码实现
- 函数调用.call来改变内部的this指向,所以我们可以在Function.prototype中再新增添一个方法作为我们自己的call
var value = 3;
var obj = {
value:4
}
function test( ) {
console.log(this.value);
}
Function.prototype.newCall = function ( ) {
console.log(this);
}
test.newCall(obj); // test
- 我们发现此时输出的this即为当前被调用的函数,所以如果我们在这个对象上添加一个fn属性,然后把函数赋给这个属性,然后调用这个属性,则可以直接在内部改变this指向
var value = 3;
var obj = {
value:4
}
function test( ) {
console.log(this.value);
}
Function.prototype.newCall = function ( that ) {
console.log(this);
console.log(that);
that.fn = this;
that.fn();
}
test.newCall(obj); // test obj 4
-
再对这个函数进行完善,将添加到对象中的东西删除掉
-
考虑解构赋值的情况( ES6 的不定参数 …args,在函数中args以数组形式存在, …args为拓展参数)
…[ 2,3,6,“ss”] => 2 3 6 “ss”
-
考虑错误处理的情况,当非object时或者
-
考虑返回值情况(js的函数默认返回值为undefined)
-
var value = 3;
var obj = {
value:4
}
function test( a,b,c ) {
console.log(a+b+c);
console.log(this.value)
}
Function.prototype.newCall = function ( that,...args ) {
if(that.toString() !== "[object object]"){
throw that+" is not an Object";
}
that = that || window; // that为空(undefined null) 则为window
that.fn = this;
const result = that.fn(...args);
delete that.fn;
return result;
}
m = test.newCall(obj,1,2,3); // 6 4
console.log(m); // undefined
apply.(obj,[arg])
apply的用法
apply在用法上和call基本相同,唯一不同的是apply中第二个参数为一个参数数组
Function.prototype.newApply = function( that , arg){
if(that.toString() !== "[object object]"){
throw that+" is not an Object";
}
that = that || window; // that为空(undefined null) 则为window
that.fn = this;
arg = arg || [];
const result = that.fn(...arg);
delete that.fn;
return result;
}
bind.(obj,arg1,arg2,arg3…)
bind的参数同call相同,但是bind是返回一个改变this指向后的函数实例;
所以利用闭包返回一个匿名函数,内部调用这个新函数
bind需要实现两种形式的赋值
- 类似于bind(context,arg1,arg2…)的解构赋值
- 返回函数实例,再进行调用
Function.prototype.newBind = function ( that,...argsA ) {
if(that.toString() !== "[object object]"){
throw that+" is not an Object";
}
that = that || window;
const Fn = this;
return function ( ...argsB ) {
let args = argsB.length == 0 ? argsA : argsB; // 如果B不存在 传入的为A的参数
return Fn.apply(that,args);
};
}
Fn为当前函数,然后使用apply将that新指向传入,又因为args在函数中是以数组的形式存在,所以即可实现
参考
- https://blog.csdn.net/weixin_40803505/article/details/85254578
- https://www.jianshu.com/p/5afd6674f352
- https://zhuanlan.zhihu.com/p/69070129