call、apply、bind

callapplybind 的作用都是修改函数的 this 指向,只是使用的方式略有不同。

$$jsdemo$$
$$edit$$
function sayHello() {
    alert(this.word)
}

const mingren = {
    word: "你好,我是鸣人。",
}

const kenan = {
    word: "你好,我是柯南。",
}

sayHello.call(mingren) // 你好,我是鸣人。
sayHello.call(kenan) // 你好,我是柯南。

sayHello.apply(mingren) // 你好,我是鸣人。
sayHello.apply(kenan) // 你好,我是柯南。

const hiMr = sayHello.bind(mingren)
const hiKn = sayHello.bind(kenan)

hiMr() // 你好,我是鸣人。
hiKn() // 你好,我是柯南。

callapply 是在修改 this 指向的同时直接调用函数,而 bind 是返回了一个修改 this 指向后的函数。

传递参数

当函数有参数时,这三者的传递方法也略有不同。

$$jsdemo$$
$$edit$$
function sayHello(name1, name2) {
    alert(`${name1}、${name2}。${this.word}`)
}

const mingren = {
    word: "你好,我是鸣人。",
}

sayHello.call(mingren, "小明", "小红") // 小明、小红。你好,我是鸣人。

sayHello.apply(mingren, ["小明", "小红"]) // 小明、小红。你好,我是鸣人。

const hiMr = sayHello.bind(mingren)

hiMr("小明", "小红") // 小明、小红。你好,我是鸣人。

实例

装饰器的 this

装饰器是我们常用的设计模式之一,但一不小心容易“丢失” this

$$jsdemo$$
$$edit$$
function decorator(func) {
    return function (...args) {
        alert("开始运行")
        return func(...args)
    }
}

const work = {
    base: 10,
    sum(x, y) {
        alert(this.base + x + y)
    },
}

// 13
work.sum(1, 2)
work.sum = decorator(work.sum)

// 你好
// NaN,因为此时 sum 函数中的 this “丢失”
work.sum(1, 2)

此时,我们就可以用 call、apply、bind 绑定 this

function decorator(func) {
    return function (...args) {
        alert("开始运行")

        // return func.call(this, ...args)
        // return func.apply(this, args)
        return func.bind(this)(...args)
    }
}

const work = {
    base: 10,
    sum(x, y) {
        alert(this.base + x + y)
    },
}

// 13
work.sum(1, 2)
work.sum = decorator(work.sum)

// 你好
// 13
work.sum(1, 2)

练习

  1. 补全以下代码。
const obj = {
    x: 100,
}

function sum(n) {
    return this.x + n
}

// 分别使用 call、apply、bind
// 三种方法绑定 obj 并执行 sum

$$answer

$$jsdemo$$
$$edit$$
const obj = {
    x: 100,
}

function sum(n) {
    return this.x + n
}

alert(sum.call(obj, 50)) // 150
alert(sum.apply(obj, [50])) // 150
alert(sum.bind(obj)(50)) // 150

$$

  1. 以下代码的执行结果偏离了预期,尝试修改一下,看看你能想到几种方法?
$$jsdemo$$
$$edit$$
const mingren = {
    name: "鸣人",
    sayHello() {
        console.log(`你好,我叫${this.name}`)
    },
}

// 修改以下代码
// 使其执行结果为:你好,我叫鸣人
setTimeout(mingren.sayHello, 3000)

$$answer

方法 1:放到函数中

$$jsdemo$$
$$edit$$
const mingren = {
    name: "鸣人",
    sayHello() {
        console.log(`你好,我叫${this.name}`)
    },
}

// 修改以下代码
// 使其执行结果为:你好,我叫鸣人

// 方法 1:放到函数中
setTimeout(() => mingren.sayHello(), 3000)

方法 2:使用 bind

$$jsdemo$$
$$edit$$
const mingren = {
    name: "鸣人",
    sayHello() {
        console.log(`你好,我叫${this.name}`)
    },
}

// 修改以下代码
// 使其执行结果为:你好,我叫鸣人

// 方法 2:使用 bind
setTimeout(mingren.sayHello.bind(mingren), 3000)

$$