概要
本章主要记录一下手写call 、 apply、 bind 函数的思路
一、call()
在使用一个指定的this和若干个指定的参数值的前提下调用某个函数或方法
示例:
let foo = {
value: 1
};
function bar() {
console.log(this.value)
}
bar.call(foo); // 1
在以上例子中,我们能确定两点:1. call改变了this的指向,指向到foo;2.bar 函数执行了;
那call() 函数执行的原理是什么呢?
思路:
- 判断执行对象是否为函数
- 获取执行函数的参数
- 传入值判断,是否有值,如果没有,默认为全局 即 window
- 执行对象挂载在上下文之上
- 在上下文中调用执行对象并且传入执行参数
- 将上下文复原,删除新增临时属性
- 返回5 的结果
代码实现:
//输入 : 上下文 执行函数的参数
//输出 : 执行结果
const myCall = function (context){
//1. 判断执行对象是否为函数
if(typeof this !== 'function') {
console.error('this is not a function')
}
//2. 获取执行函数的参数
let args = [...arguments].slice(1),
result = null;
//3. 传入值判断,是否有值,如果没有,默认为全局 即window
if(!context){
context = window;
}
// 4. 执行对象挂载在上下文之上
context.fn = this;
// 5. 在上下文中调用执行对象并且传入执行参数
result = context.fn(...args);
// 6. 将上下文复原,删除新增临时属性
delete context.fn
// 7. 返回5的结果
return result;
}
二、apply()
apply 和 call 执行思路一致,但是入参不一样。apply 入参是数组。
代码实现
const myApply = function(context) {
// 1. 判断执行对象是否为函数
if (typeof this !== 'function') {
console.error('this is not a function');
}
// 2. 获取执行函数的参数
let args = arguments[1],
result = null;
// 3. 传入值判断,是否有值,如果没有,默认为全局即window
if (!context) {
context = window;
}
// 4. 执行对象挂载在上下文之上
context.fn = this;
// 5. 在上下文中调用执行对象并且传入执行参数
if (args) {
result = context.fn(...args);
} else {
result = context.fn();
}
// 6. 将上下文复原,删除新增临时属性
delete context.fn;
// 7. 返回5的结果
return result;
}
三、bind()
bind() 传参一致,但是返回的是待执行的函数
const myBind = function (context) {
// 1. 判断执行对象是否为函数
if (typeof this !== 'function') {
console.error('this is not a function')
}
// 2. 获取参数
let args = [...arguments].slice(1),
fn = this;
return function Fn() {
// 3. 根据调用方,确定最终返回值
return fn.apply(
this instanceof Fn ? this : context,
args.concat(...arguments)
)
}
}
小结
三者对比
共同点:
- 都是用来改变函数的执行上下文(this 指向)。
- 都可以传递参数给函数。
- 都是函数的原型方法,可以在任何函数上使用。
区别:
-
参数传递方式:
call
和apply
都可以传递参数给函数,但是它们的参数传递方式不同。call
方法接受一系列参数,直接作为函数的参数传入;apply
方法接受一个数组,将数组元素作为函数的参数传入。bind
方法创建一个新函数,传递给bind
的参数会被作为绑定函数的前置参数,调用绑定函数时再加上调用时的参数。
-
立即执行 vs 延迟执行:
call
和apply
方法会立即执行函数,并返回函数的执行结果。bind
方法创建一个新函数,需要显式调用才会执行。
-
返回值:
call
和apply
方法直接执行函数并返回函数的执行结果。bind
方法返回一个新函数,不会立即执行,需要在之后调用。
-
改变函数执行上下文的方式:
call
方法接受一个上下文对象和一系列参数,将函数的执行上下文指向该对象。apply
方法同样接受一个上下文对象和参数数组,也将函数的执行上下文指向该对象。bind
方法接受一个上下文对象,返回一个新函数,新函数的执行上下文会被永久绑定到上下文对象。
-
使用场景:
call
和apply
更适合在已知参数数量时使用,如将一个对象方法应用于另一个对象。bind
更适合在预先设定一部分参数,以后再调用函数时使用,例如创建事件处理程序。