(本節內容摘自:Javascript設計模式與開發實踐一書,做爲本身的筆記保存,但願對有須要的朋友有用)html
this、call和apply在Javascript編程中應用很是普遍,因此,咱們必須先了解它們。編程
Javascript中的this老是指向一個對象,而它又是基於執行環境動態綁定,如下有4中狀況能夠用來分析。設計模式
當函數做爲對象的方法時,this指向該對象,看下面的代碼:數組
var obj = { a: 1, getA: function(){ alert(this === obj); //true alert(this.a); //1 } }; obj.getA();
看成爲普通函數調用時,此時的this老是指向全局對象window瀏覽器
window.name = 'globalName'; var getName = function(){ return this.name; }; console.log(getName()); //globalName
下面來一個實際的例子,咱們在一個div節點內部,定義一個局部的callback方法,當這個方法被當作普通函數調用時,callback內部的this就指向了window,以下代碼:app
<html> <body> <div id="div1">我是一個Div</div> </body> <script> window.id = 'window'; document.getElementById('div1').onclick = function(){ alert(this.id); //div1 var callback = function(){ alert(this.id); //window } callback(); }; </script> </html>
咱們能夠將callback函數分別寫成alert(this === callback)和alert(this === window)來測試下。函數
那咱們如何來解決這個問題呢,咱們可使用一個變量來保存div節點的引用測試
<html> <body> <div id="div1">我是一個Div</div> </body> <script> window.id = 'window'; document.getElementById('div1').onclick = function(){ var that = this; alert(that.id); //div1 var callback = function(){ alert(that.id); //div1 } callback(); }; </script> </html>
在ES5的strict模式,這種this不會再指向window,而是undefined
this
function func(){ "use strict"; alert(this); //undefined } func();
構造器的調用,當咱們使用new運算符調用函數時,總會返回一個對象,那麼構造器裏的this就指向這個對象spa
var myClass = function(){ this.name = 'Kaindy'; }; var obj = new myClass(); alert(obj.name); //Kaindy
Function.prototype.call或Function.prototype.apply調用,能夠動態的改變傳入函數的this
var obj1 = { name: 'Kaindy', getName: function(){ return this.name; } }; var obj2 = { name: 'anne'; } console.log(obj1.getName()); //Kaindy console.log(obj1.getName.call(obj2)); //anne
OK,咱們再來看下下面的例子,丟失的this:
var obj = { myName: 'Kaindy', getName: function(){ return this.myName; } }; console.log(obj.getName()); //Kaindy var getName2 = obj.getName; console.log(getName2()); //undefined
當obj調用其方法getName時,此時的this指向obj對象,因此打印出obj對象的myName屬性值,而後當把對象obj的getName方法賦值給對象getName2時,此時是普通函數調用,this就指向了全局window對象,因此會打印出undefined。
下面咱們來分析下call和apply,它們是ES3爲Function的原型定義的兩個方法,它們的做用同樣,區別只是在傳入的參數的不一樣。
apply接受兩個參數,第一個指定函數體內this對象的指向,第二個是集合,能夠是數組也能夠是類數組,apply方法把集合中的元素做爲參數傳遞給被調用的函數
var func = function(a, b, c){ alert([a, b, c]); //[1, 2, 3] }; func.apply(null, [1, 2, 3]);
call傳入的參數不是固定的,第一個參數與apply相同,表明函數體內的this指向,從第二個參數開始,每一個參數被依次傳入函數
var func = function(a, b, c){ alert([a, b, c]); //[1, 2, 3] }; func.call(null, 1, 2, 3);
在Javascript內部,參數是用數組來表示的,因此,apply比call的使用率更高,若是咱們明確參數的數量,想一目瞭然的表達形參與實參的對應關係,那麼咱們就使用call。
在使用apply或者call時,若是第一個參數使用null,那麼函數體內的this會指向默認的宿主對象,在瀏覽器中就是window,但在嚴格模式下,仍然爲null。
下面咱們來看看apply和call的實際用途
一、改變this指向
var obj1 = { name: 'sven'; }; var obj2 = { name: 'anne'; }; window.name = 'window'; var getName = function(){ alert(this.name); }; getName(); //window getName.call(obj1); //sven getName.call(obj2); //anne
當執行getName.call(obj1)的時候,getName()函數體內的this就指向了obj1對象
咱們再來看一個在實際開發中可能會遇到的例子,如頁面中有一個id爲div1的區塊
document.getElementById('div1').onclick = function(){ alert(this.id); //此處的this指向document.getElementById('div1')產生對象,輸出div1 var func = function(){ alert(this.id); //此處的this就指向全局對象window了,故輸出undefined }; func(); }
上面的代碼中,事件函數有一個內部函數func,調用此函數時,this就指向了window,咱們能夠用call來修正它
document.getElementById('div1').onclick = function(){ alert(this.id); //div1 var func = function(){ alert(this.id); //div1 }; func.call(this); }
二、Function.prototype.bind
大部分瀏覽器都實現了內置的Function.prototype.bind,用來指定函數內部的this指向。(這個內容不知道爲何要寫,因此就略掉)
三、借用其餘對象的方法
借用方法的第一種場景是「借用構造函數」,經過它能夠實現一些相似繼承的效果
var A = function(name){ this.name = name; }; var B = function(){ A.apply(this, arguments); }; B.prototype.getName = function(){ return this.name; }; var b = new B('sven'); console.log(b.getName()); //sven
第二種場景是對函數的參數列表arguments進行一些操做,arguments並不是真正的數組,若是咱們想往arguments中添加一些元素,就能夠借用Array.prototype.push方法
(function(){ Array.prototpye.push.call(argumets, 3); console.log(arguments); [1,2,3] })(1, 2);