「干货」细说 call、apply 以及 bind 的区分和用法

媒介

上一篇文章 《「前端面试题系列4」this 的道理以及用法》 中,提到了 call 和 apply。

它们最主要的作用,是转变 this 的指向。在日常平凡的工作中,除了在写一些基本类,或许公用库要领的时刻会用到它们,其他时刻 call 和 apply 的运用场景并不多。

不过,倏忽碰到的时刻,须要想一下才转过弯来。所以本日,就让我们好好地探讨一下,这两个要领的区分以及一些妙用。末了,还会引见与之用法相似的 bind 的要领。

call 和 apply 的共同点

它们的共同点是,都能够转变函数实行时的上下文,将一个对象的要领交给另一个对象来实行,而且是马上实行的。

为什么要转变实行上下文?举一个生涯中的小例子:日常平凡没时间做饭的我,周末想给孩子炖个腌笃鲜试试。然则没有合适的锅,而我又不想出去买。所以就问邻人借了一个锅来用,如许既达到了目标,又节省了开支,一石二鸟。

转变实行上下文也是一样的,A 对象有一个要领,而 B 对象由于某种原因,也须要用到一样的要领,那末这时候刻我们是零丁为 B 对象扩大一个要领呢,照样借用一下 A 对象的要领呢?当然是借用 A 对象的啦,既完成了需求,又减少了内存的占用。

别的,它们的写法也很相似,挪用 call 和 apply 的对象,必需是一个函数 Function。接下来,就会说到详细的写法,那也是它们区分的主要表现。

call 和 apply 的区分

它们的区分,主要表现在参数的写法上。先来看一下它们各自的详细写法。

call 的写法

Function.call(obj,[param1[,param2[,…[,paramN]]]])

须要注重以下几点:

  • 挪用 call 的对象,必需是个函数 Function。
  • call 的第一个参数,是一个对象。 Function 的挪用者,将会指向这个对象。假如不传,则默以为全局对象 window。
  • 第二个参数最先,能够吸收恣意个参数。每一个参数会映射到响应位置的 Function 的参数上。然则假如将一切的参数作为数组传入,它们会作为一个团体映射到 Function 对应的第一个参数上,以后参数都为空。
function func (a,b,c) {}

func.call(obj, 1,2,3)
// func 吸收到的参数实际上是 1,2,3

func.call(obj, [1,2,3])
// func 吸收到的参数实际上是 [1,2,3],undefined,undefined

apply 的写法

Function.apply(obj[,argArray])

须要注重的是:

  • 它的挪用者必需是函数 Function,而且只吸收两个参数,第一个参数的划定规矩与 call 一致。
  • 第二个参数,必需是数组或许类数组,它们会被转换成类数组,传入 Function 中,而且会被映射到 Function 对应的参数上。这也是 call 和 apply 之间,很主要的一个区分。
func.apply(obj, [1,2,3])
// func 吸收到的参数实际上是 1,2,3

func.apply(obj, {
    0: 1,
    1: 2,
    2: 3,
    length: 3
})
// func 吸收到的参数实际上是 1,2,3

什么是类数组?

先说数组,这我们都熟习。它的特性有:能够经由过程角标挪用,如 array[0];具有长度属性length;能够经由过程 for 轮回或forEach要领,举行遍历。

那末,类数组是什么呢?望文生义,就是具有与数组特性相似的对象。比方,下面的这个对象,就是一个类数组。

let arrayLike = {
    0: 1,
    1: 2,
    2: 3,
    length: 3
};

类数组 arrayLike 能够经由过程角标举行挪用,具有length属性,同时也能够经由过程 for 轮回举行遍历。

类数组,照样比较经常运用的,只是我们日常平凡能够没注重到。比方,我们猎取 DOM 节点的要领,返回的就是一个类数组。再比方,在一个要领中运用 arguments 猎取到的一切参数,也是一个类数组。

然则须要注重的是:类数组没法运用 forEach、splice、push 等数组原型链上的要领,毕竟它不是真正的数组。

call 和 apply 的用处

下面会离别枚举 call 和 apply 的一些运用场景。声明:例子中没有哪一个场景是必需用 call 或许必需用 apply 的,只是个人习气这么用罢了。

call 的运用场景

1、对象的继续。以下面这个例子:

function superClass () {
    this.a = 1;
    this.print = function () {
        console.log(this.a);
    }
}

function subClass () {
    superClass.call(this);
    this.print();
}

subClass();
// 1

subClass 经由过程 call 要领,继续了 superClass 的 print 要领和 a 变量。另外,subClass 还能够扩大本身的其他要领。

2、借用要领。还记得适才的类数组么?假如它想运用 Array 原型链上的要领,能够如许:

let domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

如许,domNodes 就能够运用 Array 下的一切要领了。

apply 的一些妙用

1、Math.max。用它来猎取数组中最大的一项。

let max = Math.max.apply(null, array);

同理,要猎取数组中最小的一项,能够如许:

let min = Math.min.apply(null, array);

2、完成两个数组兼并。在 ES6 的扩大运算符涌现之前,我们能够用 Array.prototype.push来完成。

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];

Array.prototype.push.apply(arr1, arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]

bind 的运用

末了来说说 bind。在 MDN 上的诠释是:bind() 要领建立一个新的函数,在挪用时设置 this 关键字为供应的值。并在挪用新函数时,将给定参数列表作为原函数的参数序列的前多少项。

它的语法以下:

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

bind 要领 与 apply 和 call 比较相似,也能转变函数体内的 this 指向。差别的是,bind 要领的返回值是函数,而且须要稍后挪用,才会实行。而 apply 和 call 则是马上挪用。

来看下面这个例子:

function add (a, b) {
    return a + b;
}

function sub (a, b) {
    return a - b;
}

add.bind(sub, 5, 3); // 这时候,并不会返回 8
add.bind(sub, 5, 3)(); // 挪用后,返回 8

假如 bind 的第一个参数是 null 或许 undefined,this 就指向全局对象 window。

总结

call 和 apply 的主要作用,是转变对象的实行上下文,而且是马上实行的。它们在参数上的写法略有区分。

bind 也能转变对象的实行上下文,它与 call 和 apply 差别的是,返回值是一个函数,而且须要稍后再挪用一下,才会实行。

末了,分享一个在知乎上看到的,关于 call 和 apply 的便利记忆法:

猫吃鱼,狗吃肉,奥特曼打小怪兽。

有天狗想吃鱼了

猫.吃鱼.call(狗,鱼)

狗就吃到鱼了

猫成精了,想打怪兽

奥特曼.打小怪兽.call(猫,小怪兽)

猫也能够打小怪兽了

《「干货」细说 call、apply 以及 bind 的区分和用法》

PS:迎接关注我的民众号 “超哥前端小栈”,交换更多的主意与手艺。

《「干货」细说 call、apply 以及 bind 的区分和用法》

    原文作者:micherwa
    原文地址: https://segmentfault.com/a/1190000018017796
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞