bind、call、apply 的区别与实现
理解三种改变 this 指向的方法及手写 bind 实现
问题
bind、call、apply 有什么区别?如何实现一个 bind?
解答
作用
这三个方法都用于改变函数执行时的 this 指向。
看一个典型场景:
var name1 = "lucy";
var obj = {
name1: "martin",
say: function() {
console.log(this.name1);
}
}
obj.say(); // martin
setTimeout(obj.say, 0); // lucy
定时器中的回调函数在全局上下文执行,this 指向 window。使用 bind 可以解决:
setTimeout(obj.say.bind(obj), 0); // martin
注意:如果使用 const name1 = "lucy",setTimeout 会输出 undefined,因为 const/let 声明的变量不会挂载到 window 上,只有 var 会。
apply
接收两个参数:this 指向和参数数组,立即执行函数。
function fn(...args) {
console.log(this, args);
}
let obj = { myname: "张三" }
fn.apply(obj, [1, 2]); // this 指向 obj,输出 {myname: "张三"} [1, 2]
fn(1, 2); // this 指向 window
第一个参数为 null 或 undefined 时,this 指向 window:
fn.apply(null, [1, 2]); // this 指向 window
fn.apply(undefined, [1, 2]); // this 指向 window
call
接收参数列表,立即执行函数。
function fn(...args) {
console.log(this, args);
}
let obj = { myname: "张三" }
fn.call(obj, 1, 2); // this 指向 obj,输出 {myname: "张三"} [1, 2]
fn(1, 2); // this 指向 window
同样,第一个参数为 null 或 undefined 时指向 window。
bind
返回一个新函数,不立即执行,可以分多次传参。
function fn(...args) {
console.log(this, args);
}
let obj = { myname: "张三" }
const bindFn = fn.bind(obj); // 返回新函数
bindFn(1, 2); // this 指向 obj
fn(1, 2); // this 仍指向 window
支持分次传参:
fn.bind(obj, 1)(2); // 等同于 fn.bind(obj, 1, 2)()
实现 bind
实现要点:修改 this 指向、支持分次传参、兼容 new 关键字。
Function.prototype.myBind = function (context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {
throw new TypeError("Error");
}
// 获取参数
const args = [...arguments].slice(1);
const fn = this;
return function Fn() {
// 如果作为构造函数调用,this 指向实例;否则指向 context
return fn.apply(
this instanceof Fn ? this : context,
args.concat(...arguments)
);
}
}
关键点
- apply 和 call 立即执行,bind 返回新函数
- apply 接收参数数组,call 和 bind 接收参数列表
- bind 支持分次传参,参数会合并
- 第一个参数为 null/undefined 时,this 指向 window(浏览器环境)
- bind 返回的函数用 new 调用时,this 指向新实例而非绑定的对象
目录