call、apply和bind的区别和用法

127 阅读3分钟

《用得上的前端知识》系列 - 你我都很忙,能用100字说清楚,绝不写万字长文

基本概念

类数组:具备与数组特征类似的对象。它有以下特征:

  • 可以通过角标调用
  • 具有 length 属性
  • 可以通过 for 循环或 forEach 方法

call

函数说明

call 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

基本语法

function.call(thisArg, arg1, arg2, ...)

  • thisArg,在 function 函数运行时使用的 this 值
  • arg1, arg2, ... 指定的参数列表

apply

函数说明

apply 方法调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)的形式提供的参数。

基本语法

function.apply(thisArg[, argsArray])

  • thisArg,在 function 函数运行时使用的 this 值
  • argsArray,可选的,一个数组或者类数组对象。其中的数组元素将作为单独的参数传给 function 函数。

bind

函数说明

bind 方法创建一个新的函数,bind 函数被调用后,将返回一个新的函数,新创建的函数的 this 被指定为 bind 函数的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

基本语法

function.bind(thisArg[, arg1[, arg2[, ...]]])

  • 调用绑定函数时作为 this 参数传递给目标函数的值。如果使用 new 运算符构造绑定函数,则忽略该值。
  • arg1,arg2,... 当目标函数(返回函数)被调用时,被预置入绑定函数的参数列表中的值。

区别

call 和 apply 的共同点

  • 都能改变函数执行时的上下文;
  • 调用 call 或 apply 的对象,必须是一个函数;
  • 第一个参数都是对象,且 function 的调用者将会指向这个对象,如果不传就是 window。

call 和 apply 的不同点

  • 参数个数不同,call 从第 二个参数开始,可以接收任意数量的参数。每个参数会映射到 function 函数相应位置的参数上;
  • 参数类型不同,apply 只接收 2 个参数,第二个参数必须是数组或类数组。它们会被转换成类数组传入到 function 中,并且映射到对应的参数上。

call、apply、bind

  • 三者都可以改变对象的执行上下文,使用 call 和 apply 时,调用函数是立即执行的,而 bind 则是返回一个函数,需要再次调用才会执行;
  • bind 第一个参数的规则与 call、apply 相同,但对于传参则有些不同,既可以在调用 bind 函数时预传参数,也可以在调用返回函数时才传参。

1、对象继承

function animal(a, b) {
 this.type = 'animal'
    this.behavior = function(){
        console.log(this.type+" is running")
    }
}

function cat(a, b) {
    this.name = 'cat'
    //这里 call 的意思就是把 animal 的方法应用到 cat 这个对象身上
    //所以 cat 就继承了 animal 的方法
    animal.call(this);
}

console.log(new cat())
// cat {name: "wsscat", type: "animal", behavior: ƒ}
// 当然,上面的call方法也能换成apply
// 或者换成bind,不过要调用以下返回函数:animal.call(this)()


// ------------------ 继承的优化 --------------------

如果构造函数this绑定太多属性(比如一些共同方法),在实例化后会造成浪费,为此我们一般会使用原型链来优化,但是使用原型链之后我们的apply和call的继承方法就会失效
为此我们一般使用混合的写法,使用原型链和(apply或者call)方法进行继承

function animal(type) {
    this.type = type
    this.behavior = function() {
        console.log(this.type + " is running")
    }
}
animal.prototype.action = function() {
    console.log("running")
}

function cat(name, type) {
    this.name = name
    animal.call(this, type) // 让父的属性创建在子的this上
}

cat.prototype = new animal(); // 让子的原型链指向父的实例(父实例化的对象)
console.log(new cat('wsscat', 'cute'));
(new cat('wsscat')).action() //running

2、借用方法

// 比如,我们想使用 Array 原型链上的方法,可以这样写:
let domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));