全部 / 前端 / 技术 · 2022年6月19日 0

40 – 真正理解 Bind、Call 和 Apply

广告位招租 (vx: ghostcode, 备注:网站广告)

原文地址:https://dev.to/bhagatparwinder/truly-understand-bind-call-apply-21da

Bind

JavaScript 中的 this 扮演者重要的角色,在 JavaScript 中它是依据函数是怎么被调用的而不是它在哪声明的(箭头函数则表现为相反)。

我们举一个例子来示范一下 this 是如何工作的:

const sayGreeting = {
    name: "Parwinder",
    hello: function() {
        return `Hello, ${this.name}`;
    }
}

console.log(sayGreeting.hello()); // Hello, Parwinder

hello 方法可以获取 sayGreeting 上的 name 属性,当我用 sayGreeting 调用它时,它是运行在 sayGreeting 对象上下文中的。

相反如果我这样做:

const sayGreeting = {
    name: "Parwinder",
    hello: function() {
        return `Hello, ${this.name}`;
    }
}

const hello = sayGreeting.hello;

console.log(hello === sayGreeting.hello); // true
console.log(hello()); // Hello, undefined

尽管 hello 变量与 sayGreeting 对象上的 hello 方法相等,但它的执行上下文并不是在 sayGreeting 中,而是在 window 或全局状态中。

bind 方法允许我们绑定上下文,它返回一个新的方法且上下文绑定为我们传递给 bind 函数的内容。

const sayGreeting = {
    name: "Parwinder",
    hello: function() {
        return `Hello, ${this.name}`;
    }
}

const hello = sayGreeting.hello.bind(sayGreeting);
console.log(hello()); // Hello, Parwinder

实际业务中哪里需要使用 bind?

上面的所有例子,数据的获取和方法的调用都在一个对象上 bind 的作用并不明显。可有时候当你需要向一个对象借一个方法但运行上下文需要在另一个对象中时,你就需要使用 bind

const sayGreeting = {
    name: "Parwinder",
    hello: function () {
        return `Hello, ${this.name}`;
    }
}

const nameObject = {
    name: "Lauren"
}

const hello = sayGreeting.hello.bind(nameObject);

console.log(hello()); // Hello, Lauren

sayGreeting 对象上有 hello 方法,所以在 nameObject 对象上就没有必要再写一个相同的方法。我可以向 sayGreeting 对象借用它然后运行在 nameObject 上下文中。

Call

callapplybind 是有区别的,bind 返回一个新的方法而 callapply 则立即调用执行方法。callthis 作为第一个参数,其他参数需要一个个的传递。它们都会传递到我们调用的函数中:

const sayGreeting = {
    name: "Parwinder",
    hello: function () {
        return `Hello, ${this.name}`;
    }
}

console.log(sayGreeting.hello.call(sayGreeting)); // Hello, Parwinder

带有其它参数:

const sayGreeting = {
    name: "Parwinder",
    hello: function (trait, color) {
        return `Hello, ${this.name}. I see you ${trait} ${color}. It is my favorite too!`;
    }
}

console.log(sayGreeting.hello.call(sayGreeting, "like", "red"));
// Hello, Parwinder. I see you like red. It is my favorite too!

Apply

尽管 applycall 类似都是直接执行函数,但它接受的是一个数组作为第二个参数而不是逗号分隔的值。

const sayGreeting = {
    name: "Parwinder",
    hello: function () {
        return `Hello, ${this.name}`;
    }
}

console.log(sayGreeting.hello.apply(sayGreeting)); // Hello, Parwinder

当没有其余参数时 applycall 没有区别,但是当我们有其余参数时,使用方法就有点区别:

const sayGreeting = {
    name: "Parwinder",
    hello: function (trait, color) {
        return `Hello, ${this.name}. I see you ${trait} ${color}. It is my favorite too!`;
    }
}

console.log(sayGreeting.hello.apply(sayGreeting, ["like", "red"]));
// Hello, Parwinder. I see you like red. It is my favorite too!

apply 使得传递多个参数变得更容易些,但是现在 ES6 中使用扩展运算符传递多个参数也很容易了。

前端黑板报