js中 call() 和 apply() 方法的區別和用法詳解


1、定義

  每個函數都包含倆個非繼承而來的方法:call()apply()

   call apply 可以用來重新定義函數的的執行環境,也就是 this 的指向;call 和 apply 都是為了改變某個函數運行的 context , 即上下文而存在的,就是為了改變函數體內部 this 的指向。

 語法:

 call()

   調用一個對象的方法,用另一個對象替換當前對象,可以繼承另外一個對象的屬性,它的語法是:

Function.call(obj,params...);
  • obj:這個對象將代替Function類里this對象
  • params:一串參數列表

 說明call方法可以用來代替另一個對象調用一個方法,call方法可以將一個函數的對象上下文從初始的上下文改變為obj指定的新對象,如果沒有提供obj參數,那么Global對象被用於obj。  

 apply()

  和call()方法一樣,只是參數列表不同,語法:

Function.apply(obj,[argArray]);
  • obj:這個對象將代替Function類里this對象
  • argArray:這個是數組,它將作為參數傳給Function

 說明:如果argArray不是一個有效數組或不是arguments對象,那么將導致一個TypeError,如果沒有提供argArrayobj任何一個參數,那么Global對象將用作obj

2、相同點 

  call()apply()方法的相同點就是這兩個方法的作用是一樣的。都是在特定的作用域中調用函數,等於設置函數體內this對象的值,以擴充函數賴以運行的作用域

  一般來說,this總是指向調用某個方法的對象,但是使用call()apply()方法時,就會改變this的指向,看個例子:

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

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

console.log(add.call(sub, 2, 1));//3

  為什么add.call(sub, 2, 1)的執行結果是3呢,因為call()方法改變了this的指向,使得sub可以調用add的方法,也就是用sub去執行add中的內容,再來看一個例子:

function People(name, age) {
    this.name = name;
    this.age = age;
}

function Student(name, age, grade) {
    People.call(this, name, age);
    this.grade = grade;
}

var student = new Student('小明', 21, '大三');
console.log(student.name + student.age + student.grade);//小明21大三  在這個例子中,我們並沒有給Studentnameage賦值,但是存在這兩個屬性的值,這還是要歸功於call()方法,它可以改變this的指向。

  在這個例子里,People.call(this, name, age);中的this代表的是Student,這也就是之前說的,使得Student可以調用People中的方法,因為People中有this.name = name;等語句,這樣就將nameage屬性創建到了Student中。

  總結一句話就是call()可以讓括號里的對象來繼承括號外函數的屬性

  至於apply()方法作用也和call()方法一樣,可以這么寫:

People.apply(this, [name, age]);

  或者這么寫:

People.apply(this, arguments);

  在這里arguments[name, age]是等價的。

3、不同點

  從定義中也可以看出來,call()apply()的不同點就是接收參數的方式不同

  • apply()方法接收兩個參數,一個是函數運行的作用域(this),另一個是參數數組。
  • call()方法不一定接受兩個參數,第一個參數也是函數運行的作用域(this),但是傳遞給函數的參數必須列舉出來。

  在給對象參數的情況下,如果參數的形式是數組的時候,比如之前apply()方法示例里面傳遞了參數arguments,這個參數是數組類型,並且在調用Person的時候參數的列表是對應一致的(也就是PersonStudent的參數列表前兩位是一致的)就可以采用apply()方法。

  但是如果Person的參數列表是這樣的(age,name),而Student的參數列表是(name,age,grade),這樣就可以用call()方法來實現了,也就是直接指定參數列表對應值的位置Person.call(this,age,name)

4、拓展  

 apply()的其他用法

  apply有一個巧妙的用處,就是可以將一個數組默認的轉換為一個參數列表([param1,param2,param3]轉換為param1,param2,param3),借助apply的這點特性,所以就有了以下高效率的方法:

 1)Math.max可以實現得到數組中最大的一項

  因為Math.max參數里面不支持Math.max([param1,param2]),也就是數組,但是它支持Math.max(param1,param2,param3…),所以可以根據apply的那個特點來解決:

var array = [1, 2, 3];
var max = Math.max.apply(null, array);
console.log(max);//3
  這樣輕易的可以得到一個數組中最大的一項, apply會將一個數組裝換為一個參數接一個參數的傳遞給方法,這塊在調用的時候第一個參數給了一個 null,這個是因為沒有對象去調用這個方法,我們只需要用這個方法幫我運算,得到返回的結果就行,所以直接傳遞了一個 null過去,當然,第一個參數使用 this也是可以的:
var array = [1, 2, 3];
var max = Math.max.apply(this, array);
console.log(max);//3

  使用this就相當於用全局對象去調用Math.max,所以也是一樣的。

 2)Math.min可以實現得到數組中最小的一項

  同樣的Math.minMath.max是一個思想:

var array = [1, 2, 3];
var min = Math.min.apply(null, array);
console.log(min);//1

  當然,apply的第一個參數可以用null也可以用this,這個是和Math.max一樣的。

 3)Array.prototype.push可以實現兩個數組合並

  同樣的,push方法沒有提供push一個數組,但是它提供了push(param1,param,…paramN)所以同樣也可以通過apply來裝換一下這個數組,即:

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

  可以這樣理解,arr1調用了Arraypush方法,參數是通過apply將數組裝換為參數列表的集合,其實,arr1也可以調用自己的push方法:

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

  也就是只要有push方法,arr1就可以利用apply方法來調用該方法,以及使用apply方法將數組轉換為一系列參數,所以也可以這樣寫:

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

 總結

  一般在目標函數只需要n個參數列表,但是不接收一個數組的形式([param1[,param2[,…[,paramN]]]]),我們就可以通過apply的方式來巧妙地解決。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM