JavaScript設計模式之二:this、call和apply

(本節內容摘自:Javascript設計模式與開發實踐一書,做爲本身的筆記保存,但願對有須要的朋友有用)html

this、call和apply在Javascript編程中應用很是普遍,因此,咱們必須先了解它們。編程

1、this

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。

2、call和apply

下面咱們來分析下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);
相關文章
相關標籤/搜索