JS——call()和apply()的用法
在上一篇文章中我們對js中的this指向問題進行了介紹,其中涉及到call()和apply()方法,在這篇文章中,我們將對call()和apply()進行探討;
首先我們來說說這兩種方法的共同點:
1. 每個函式都包含兩個非繼承而來的方法:call()方法和apply()方法。
2.這兩個方法的作用是一樣的。 都是在特定的作用域中呼叫函式,等於設定函式體內this物件的值,以擴充函式賴以執行的作用域。
在JS中this總是用來指向某個方法的物件,而call()和apply()的作用就是用來改變this的指向。
call()和apply()作用完全一樣,只是接受引數的方式不一樣。
call的用法:
下面的程式碼演示了this指向問題,call()改變this指向,以及call()實現繼承
//下面這段程式碼主要演示了this指向的問題 window.color = 'red'; document.color = 'yellow'; var s1 = {color: 'blue' }; changeColor(){ console.log(this.color); } changeColor.call(); //red (預設傳遞引數) changeColor.call(window); //red changeColor.call(document); //yellow changeColor.call(this); //red changeColor.call(s1); //blue //下面我們將看看call是怎樣改變this指向的 var Pet = { words : '...', speak : function (say) { console.log(say + ''+ this.words) } } Pet.speak('Speak'); // 結果為:Speak... var Dog = { words:'Wang' } //將this的指向改變成了Dog Pet.speak.call(Dog, 'Speak'); //結果為: SpeakWang //下面這段程式碼我們再看看使用call()實現繼承 function Animal(name){ this.name = name; this.showName = function(){ console.log(this.name); } } function Cat(name){ Animal.call(this, name); } var cat = new Cat("Black Cat"); cat.showName(); //結果為: Black Cat
apply()與call()用法類似,可參考上面的例子
下面我們看看不同點,即傳入引數不同:
var func = function(arg1, arg2) {
};
//呼叫該函式的兩種不同方式
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
其中 this 是你想指定的上下文,他可以是任何一個 JavaScript 物件(JavaScript 中一切皆物件),call 需要把引數按順序傳遞進去,而 apply 則是把引數放在數組裡。
我們已經看了call()和apply()的基本用法,下面我們用一些例項鞏固一下:
1.陣列的追加
var array1 = [1,2,3];
var array2 = [a,b,c];
Array.prototype.push.apply(array1, array2);
// array1 值為 [1,2,3,a,b,c]
Array.prototype.push可以實現陣列的追加
2.獲取陣列中的最大最小值
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
number 本身沒有 max 方法,但是 Math 有,我們就可以藉助 call 或者 apply 使用其方法。
3.驗證是否為資料型別
console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]
4.類陣列使用陣列方法
首先我們要明白什麼是類陣列:
1.具有length屬性
2.按索引方式儲存資料
3.不具有陣列的push,pop等方法
常見的類陣列有:arguments,NodeList
(function(){
Array.prototype.push.call(arguments,4);
console.log(arguments);//[1, 2, 3, 4]
})(1,2,3)
Array.prototype.slice.call(document.getElementsByTagName("*"));
Javascript中存在一種名為偽陣列的物件結構。比較特別的是 arguments 物件,還有像呼叫 getElementsByTagName , document.childNodes 之類的,它們返回NodeList物件都屬於偽陣列。不能應用 Array下的 push , pop 等方法。 但是我們能通過 Array.prototype.slice.call 轉換為真正的陣列的帶有 length 屬性的物件,這樣 domNodes 就可以應用 Array 下的所有方法了。
下面是一個經典的面試題:
定義一個 log 方法,讓它可以代理 console.log 方法,常見的解決方法是:
1:
function log(msg) {
console.log(msg);
}
log(1); //1
log(1,2); //1
2:上面方法可以解決最基本的需求,但是當傳入引數的個數是不確定的時候,上面的方法就失效了,這個時候就可以考慮使用 apply 或者 call,注意這裡傳入多少個引數是不確定的
,所以使用apply是最好的,方法如下:
function log(){
console.log.apply(console, arguments);
};
log(1); //1
log(1,2); //1 2
3.接下來的要求是給每一個 log 訊息新增一個"(app)"的前輟,比如:
log("hello world"); //(app)hello world
該怎麼做比較優雅呢?這個時候需要想到arguments引數是個偽陣列,通過 Array.prototype.slice.call 轉化為標準陣列,再使用陣列方法unshift,像這樣:
function log(){
var args = Array.prototype.slice.call(arguments);
args.unshift('(app)');
console.log.apply(console, args);
};
log('hello world') //(app)hello world
以上是關於apply()和call()的一些用法,不盡詳細,歡迎指正