来聊聊 call、apply、bind 三兄贵

424 阅读4分钟

0. 写啥都得有的序

call,apply,bind, 这三个Function下的方法,作为修改this指向的三兄弟 ,这里列举三个方法的demo之前。我们来简单说一下为啥要改this指向这件事。

this 作为js中非常类似于C语言指针的东西。它所指向的准确来说是对象的上下文。改变了引用的上下文便可以调用这个上下文中的标识符(它可以是对象、变量、方法)。

如果函数挂载在一个对象上,作为对象的一个属性,就称它为对象的方法。当通过这个对象来调用函数时,该对象就是此次调用的上下文(context),也就是该函数的this的值。——《JavaScript权威指南(第六版)》p165

1. call() 调用

Function.prototype.call() call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。 注意:该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。 ----MDN Web Docs

可以使用apply()call()方法以新创建的对象为上下文执行构造函数 ——《JavaScript 高级程序(第四版)》p243

call的主要用途有三点:

  1. 直接调用函数
  2. 可改变函数内this
  3. call 主要作用可以实现继承

我们来看看demo

let o = {
    name'john'
};
function fn(a, b) {
    console.log('call'this);
    console.log(a + b)
}
fn.call(o, 12);// 改变this指向 o 并且直接调用fn()


// 实现构造函数继承
function Papa(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}
function Son(name, age, gender) {
    Papa.call(this, name, age, gender)
}
let son = new Son('john'18'M')
console.log(son)//{ name: 'john', age: 18, gender: 'M' }

2. apply() 应用

applycall 在用法上极为相识,唯一区别是在入参的形式下。从某种意义来讲,apply 更像是 call 封装的语法糖。

apply的特点: 1. 调用函数,改变内部this指向; 2. 参数必须为数组; 3. 主要应用于数组类;


var o = {
    name'jervis'
};
function fn(a) {
    console.log(this);
    console.log(a)
}
fn.apply(o);// this 指向 o
fn.apply(o, ['jervis'])

const arr = [1,2,3,4,5,6,7]
const max = Math.max.apply(Math, arr);// 因为不需要改变指向,可以直接写null 但是在严格模式下会出现错误所以建议填函数的调用者
const min = Math.min.apply(Math, arr);
console.log(`max: ${max}; min: ${min};`)//max: 7; min: 1

3. bind() 绑定

接下来重点说一下bind

作为ECMAScript 5语法 bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。 ----MDN Web Docs

bind的作用: 1. 改变原函数内部指向;(和另外两个不一样,它不会直接调用函数) 2. 返回改变指向后的拷贝

let o = {
    name'John'
};
function fn(a, b) {
    console.log('function inner'this);
    // console.log(a + b);
};
fn();// global
let f = fn.bind(o, 12);
f();// o
fn();// global

bind 的使用场景:如果有的函数不需要立即调用又想改变这个函数的内部this

根据使用场景我们假设有这样一个需求:我们需要一个用于发送短信点击后便禁用,3秒后重新开启的按钮。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button class="button">button</button>
</body>
<script>
    let btn1 = document.querySelector('button');
    console.log(btn1);
    btn1.onclick = function() {
        this.disabled = true;// this 指向btn这个按钮
        // let that = this;
        // setTimeout(function() {
        //     //that.disabled = false;// 定时器内this指window(结合事件循环宏任务)
        //     this.disabled = false;
        // }.bind(this), 2000);
        setTimeout(()=> {// 因为箭头函数中的this跟随函数声明当前的上下文,所以定时器中的this在执行时已经指向了当前上下文对象
            console.log(this)// btn1
            this.disabled = false;
        }, 2000);
    }
</script>
</html>

4. 总结

经过上面的demo我们可以总结出写满几点:

三个api的相似点: 

都可以改变函数内部this的指向

区别: 

1. call 和apply 会调用函数,并改变函数内部this指向 2. call 和apply 传递的参数不一样,call(object, param1, param2 ) apply(object, [param1, param2]) 2. bind 不会直接调用函数,不会改变函数内部this指向,但是会返回改变this指向的原函数的拷贝

主要应用场景:

1. call 经常用作继承构造函数; 2. apply 经常与数组有关系,比如借助Math实现数组求最大最小值; 3. bind 不调用函数,但是还想改变this指向,常配合定时器使用,但是在ES6普及的环境下(有了箭头函数)已经用的比较少了;

最后的最后,欢迎留言提供宝贵的阅读意见共同进步,原创不易~如果看到这里对您有点帮助请给个赞啦,嘻嘻