前言
最近在整理回顾之前的知识,故在此记录一下,众所周知,JavsScript中this是在执行上下文创建时确定的一个在执行过程中不可更改的变量,对于this不熟悉的同学可以去:理解Javascript的this。而call,apply,bind的出现则是打破了这一规则,所以本质上来说bind和call,apply的作用类似,都是用来更改函数的this值的,其中call和apply类似,只是传参的不同,前者是若干连续参数,后者则是传入一个数组,而bind则是返回一个函数,这里不多说,接下来我们开始模拟
使用call方法举个例子
以下是用原生的call方法书写的例子,可以看到bar函数的this指向foo对象,而且可以传递参数,也就是说,call干了两件事:
1.改变函数的this指向
2.传参执行函数
var foo = {
value: 1
};
function bar(n = 0) {
console.log(this.value, n);
}
bar.call(foo); // 1,0
bar.call(foo, 6); // 1,6
模拟第一步
试想我们要是想得出上一个例子的结果,可以直接在foo对象上挂载一个bar函数,也就是如下示例:
var foo = {
value: 1,
bar: function (n = 0) {
console.log(this.value, n)
}
};
foo.bar(6); // 1,6
也就是说我们把这个方法赋值给对象,然后对象调用这个函数就可以了。总结:
1.将this要指向的函数赋值给传入的对象,
2.执行此函数,
3.执行之后从对象上删除这个函数
那我们就可以实现第一版了,示例如下:
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
Function.prototype._call = function (obj) {
// this 指的是调用_call的函数
console.log(this);
// obj.fun = this
obj.fun()
delete obj.fun
}
bar._call(foo); //1
测试之后发现可以打印1,nice,简直easy啊,别急,开头咱们说了原生call是可以传参的,说干就干
模拟第二步
var foo = {
value: 1
};
function bar(n = 0) {
console.log(this.value, n);
}
Function.prototype._call = function (obj, ...args) {
// console.log(args);
// this 指的是调用_call的函数
// console.log(this);
obj.fun = this
obj.fun(...args)
delete obj.fun
}
bar._call(foo, 6);//1,6
因为是可以传入不定长度的参数的,故我使用的是es6的...args剩余参数,可以直接取到全部参数。这就完事了?桥的马得,call还有一个特点是第一个参数为null时,this指向window,并且函数时有返回值的哦。 继续往下看。
模拟第三步
先举个原生call的例子
// 被调用的函数可能有返回值
var foo = {
value: 1
};
function bar(n = 0) {
console.log(this.value, n);
return 123
}
let a = bar.call(foo)
console.log(a); // 123
//传入null时指向window
var value = 99;
var foo = {
value: 1,
};
function bar() {
console.log(this.value);
}
bar.call(null); //99
接下来咱们就开始最后的修改啦,最后代码如下:
var foo = {
value: 1
};
function bar(n = 0) {
console.log(this.value, n);
return 123
}
Function.prototype._call = function (obj, ...args) {
//传入null/undefined时this指向window
if (obj === null || obj === undefined) {
obj = window;
} else {
obj = Object(obj) || obj; //检验类型是否是对象
}
// console.log(args);
// this 指的是调用_call的函数
// console.log(this);
obj.fun = this;
const res = obj.fun(...args);
delete obj.fun;
return res
}
这就是我们的最终代码,此时
bar._call(obj,1) //1 1
let a = bar._call(foo, 1)
console.log(a); // 123
bar._call(2,1) //NaN 1
至此,我们完成了 call 的模拟实现,给自己一个赞!!!
apply的模拟实现
apply和call类似,只是传递参数的不同,apply是传递数组,我们改下_call方法
Function.prototype._apply = function (obj, arr) {
if (obj === null || obj === undefined) {
obj = window;
} else {
obj = Object(obj) || obj;
}
obj.fun = this;
arr = arr || [];
const res = obj.fun(...arr);
delete obj.fun;
return res;
};
最后
我们实现过程都解决了以下问题:
1.更改被调用函数的this
2.传递参数给被调用函数
3.返回被调用函数的结果,第一个参数为null/undefined时,让被调用函数this指向window
4.判断第一个参数的类型
以上。不忘初心,砥砺前行,欢迎各位讨论,找出更好的方法。美女镇楼: