1. 程式人生 > 實用技巧 >js中call()和apply()方法的區別和用法詳解

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

今天又碰到了JacvaScript中的call()apply()方法,然後看看學學,敲了遍程式碼,才大概對這兩個方法有些瞭解,這篇部落格是對這兩個方法的歸納整理,如果有寫的不夠詳細或者有錯誤的地方歡迎指出。

1.定義

每個函式都包含兩個非繼承而來的方法:call()方法和apply()方法。

callapply可以用來重新定義函式的執行環境,也就是this的指向;callapply都是為了改變某個函式執行時的context,即上下文而存在的,換句話說,就是為了改變函式體內部this的指向。

語法

call()

呼叫一個物件的方法,用另一個物件替換當前物件,可以繼承另外一個物件的屬性,它的語法是:

Function.call(obj[, param1[, param2[, [,...paramN]]]]);
  • 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的方式來巧妙地解決。