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
,如果沒有提供argArray
和obj
任何一個參數,那么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大三 在這個例子中,我們並沒有給Student
的name
和age
賦值,但是存在這兩個屬性的值,這還是要歸功於call()
方法,它可以改變this
的指向。
在這個例子里,People.call(this, name, age);
中的this
代表的是Student
,這也就是之前說的,使得Student
可以調用People
中的方法,因為People
中有this.name = name;
等語句,這樣就將name
和age
屬性創建到了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
的時候參數的列表是對應一致的(也就是Person
和Student
的參數列表前兩位是一致的)就可以采用apply()
方法。
但是如果Person
的參數列表是這樣的(age,name)
,而Student的參數列表是(name,age,grade)
,這樣就可以用call()
方法來實現了,也就是直接指定參數列表對應值的位置Person.call(this,age,name)
。
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.min
和Math.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
調用了Array
的push
方法,參數是通過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
的方式來巧妙地解決。