原生JS中 call() 和 apply() 的使用方法

使用说明:

与其说我们在说明 call() 和 apply() 这两个方法,不如说我们是在说:

Function.prototype.call()

Function.prototype.apply()

这两个方法,其实是一样的。
call和apply这两个方法在一开始的时候确实挺难以理解的,而且两个方法使用相似,没有太大的差别,都是为了改变 this 的指向问题,在使用方法上我们说明其中一个的时候你就会明白另一个的原理,后面我们在进行总结两者的区别到底在哪里,还有他们的巧妙用处,首先按照官方的定义是:


apply() 方法在指定 this 值和参数(参数以数组或类数组对象的形式存在)的情况下调用某个函数。

call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法.

说明摘自:https://developer.mozilla.org/zh-CN/

首先来看一下 call(obj,[arg]) 这个方法,

语法:
  1. fun.call(thisArg[, arg1[, arg2[, ...]]])
参数:

thisArg

在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。

arg1, arg2, …

指定的参数列表。

回归正题:

我们都知道变量的作用域、函数的作用域,比如:

  1. function a(name,age){
  2. console.log(this); //this 指向的是 a()
  3. this.name = "zhang";
  4. this.addAge = function(){
  5. console.info(this.name); //zhang
  6. }
  7. }
  8. function b(){
  9. this.name = "wang";
  10. console.log(this.name);//wang
  11. }

函数 a() 中的 a.addAge() 这个方法下面的 this.name 指向的是上面的 this.name = “zhang”;当然我们也可以通过 call() 或者 apply() 方法改变 addAge() 这个方法的指向,使 addAge() 的 this 指向函数 b() ; 这时我们就写到如下:

  1. function a(name,age){
  2. console.log(this); //this 指向的是 a()
  3. this.name = "zhang";
  4. this.addAge = function(){
  5. console.info(this.name); //zhang
  6. }
  7. }
  8. function b(){
  9. this.name = "wang";
  10. console.log(this.name);//wang
  11. }
  12. var fa = new a();
  13. var fb = new b();
  14. //执行函数 a.addAge()并将 a.addAge()的 this 指向 b()
  15. fa.addAge.call(fb); //"wang"

简单明了得说明了,在举个例子:

  1. function add(a, b) {
  2. alert(a + b);
  3. }
  4. function sub(a, b) {
  5. alert(a - b);
  6. }
  7. add.call(sub, 3, 1);

这个例子中的意思就是用 add 来替换 sub,add.call(sub,3,1) == add(3,1) ,所以运行结果为:alert(4); // 注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。

也可以是继承的关系或者多重继承,如下:

  1. function Animal(name) {
  2. this.name = name;
  3. this.showName = function() {
  4. alert(this.name);
  5. }
  6. }
  7. function Cat(name) {
  8. Animal.call(this, name);
  9. }
  10. var cat = new Cat("Black Cat");
  11. cat.showName();

Animal.call(this) 的意思就是使用 Animal对象代替this对象,那么 Cat中不就有Animal的所有属性和方法了吗,Cat对象就能够直接调用Animal的方法以及属性了.

下面我们来说一下区别:

我们在使用 call() 或者 apply() 这两个方法的时候,第一个参数都是一样的,其区别在于第二个参数有所不同,比如说我们需要往 call() 或者 apply() 的第二个参数传入一个数组如下:

  1. function Animal(a) {
  2. this.name = a;
  3. this.showName = function() {
  4. console.log(this.name);
  5. }
  6. }
  7. function Cat() {
  8. var arr = [1,2,3,4,5]
  9. Animal.call(this,arr);
  10. }
  11. var cat = new Cat();
  12. cat.showName();

我们在 Cat() 方法中创建了一个数组 arr = [1,2,3,4,5]; 并把 Animal() 的 this 指向了 Cat() 并传入了参数 arr, 这时我们的 cat.showName(); 打出来的 log 是:

  1. [1,2,3,4,5]

也就是我们传入的数组,说明 Animal() 在接收到 call() 传入的是 : [1,2,3,4,5]这个数组;

如果我们改用 apply(); 则会打出

  1. 1

我们在将代码修改:

  1. function Animal(a,b,c,d,e) {
  2. console.log(a,b,c,d,e);
  3. }
  4. function Cat() {
  5. var arr = [1,2,3,4,5]
  6. Animal.call(this,arr);
  7. }
  8. var cat = new Cat();
  9. cat();

这时候我们打出来如下:

  1. [1,2,3,4,5],undefined,undefined,undefined,undefined

说明 call() 传入的是一个数组对象,同事我们再改用 apply() 如下:

  1. function Animal(a,b,c,d,e) {
  2. console.log(a,b,c,d,e);
  3. }
  4. function Cat() {
  5. var arr = [1,2,3,4,5]
  6. Animal.apply(this,arr);
  7. }
  8. var cat = new Cat();

这时候我们打出来的则是:

  1. 1,2,3,4,5

这说明 call() 传入的是一个数组的对象,而 apply() 传入的是一个 数组的集合;
对此,有几种经典的使用方法就是 Math.max() 和 push()
我们要找出一个数组中的最大值使用 Math.max();

  1. Math.max(1,8,6,4,153,4);//153

但是当我们这样写的时候就会报错,如下:

  1. var arr = [1,8,6,4,153,4];
  2. Math.max(arr);

这两者的区别就在于,第一次我们传入的是数组的集合,而第二次则传入的是数组的对象,所以会报错,
所以我们就可以用到 apply() 的一些技巧,在合并数组的时候我们可以用到 concat() 这个方法合并数组,但是 concat() 是返回一个新的数组,而不是修改原本的数组,同时我们也可以:

  1. var arr = [1,2,3,4,5];
  2. var arrTwo = [7,8,2,4,6];
  3. arr.push.apply(arr,arrTwo);
  4. console.log(arr);

这样我们也可以合并两个数组。同时是在第一个数组上进行修改。
文章来自于个人的理解,如有不正之处还望指出。

评论: