關於this的全面解析(上)

關於this的全面解析(下)頁面連接segmentfault

this的調用位置

調用位置就是函數在代碼中被調用的位置(而不是聲明的位置),尋找調用位置就是尋找「函數被調用的位置」,最重要的是分析調用棧(就是爲了到達當前執行位置所調用的全部函數)。瀏覽器

function baz() {
        //當前調用棧是baz
        //當前調用位置是全局位置
        console.log('baz');
        bar(); //<--bar的調用位置
    }

    function bar() {
        //當前的調用棧是baz->bar
        //所以當前的調用位置在baz中
        console.log('bar');
        foo(); //<--foo的調用位置
    }

    function foo() {
        //當前的調用棧是baz->bar->foo
        //所以當前的調用位置在bar中
        console.log('foo');
    }
    baz(); //<-baz的調用位置

把調用棧想象成一個函數調用鏈,如上圖代碼中的樣式,可是這種方法很是麻煩而且容易出錯。另外一個查看調用棧的方法是使用瀏覽器的調試工具。app

綁定規則

首先須要找到調用位置,而後判斷尋求下列四條規則中的哪一條。函數

1 默認綁定工具

首先介紹最經常使用的函數調用類型:獨立函數調用。能夠把這條規則看做是沒法應用其餘規則時的默認規則。this

function foo() {
        console.log(this.a); //<-this指向全局做用域
    }
    var a = 2;
    foo(); //<-foo調用位置

在代碼中,foo()是直接使用不帶任何修飾的函數引用進行調用的,所以只能使用默認綁定,沒法應用其餘規則。調試

function foz() {
        "use strict";
        console.log(this.a); //<--嚴格模式下不能將全局對象用於綁定

    }
    foz(); //TypeError

2 隱藏綁定code

另外一條須要考慮的規則是調用位置是否有上下文對象,或者說是否被某個對象擁有或者包含,這種說法有時候會有誤導。對象

function foa() {
        console.log(this.a);
    }
    var foaObj = {
        a: "Hello",
        foa: foa //<--foa函數調用位置
    }
    foaObj.foa();

foa函數在嚴格意義上來講不屬於foaObj對象。然而,調用位置會使用foaObj上下文來引用函數,所以能夠判斷爲函數調用時,foaObj對象包含並引用它。ip

當函數引用有上下文對象時,隱式綁定規則會把函數調用中的this綁定到這個上下文對象。所以,調用foa函數時this被綁定到foaObj這個對象上,因此this.a 和 foaObj.a 是同樣的。

然而,有一個常見的this綁定問題就是被隱式綁定的函數會丟失綁定對象,也就是說它會應用默認綁定,從而把this綁定到全局對象或undefined上。

function fob() {
        console.log(this.a);
    }
    var fobObj = {
        a: "Hello",
        fob: fob
    }
    var focObj = fobObj.fob;
    var a = 1; //a是全局對象的屬性
    focObj();

focObj引用其實是fob函數,因此this綁定的是全局對象中的a。

3 顯式綁定

就像咱們剛纔看到的那樣,在分析隱式綁定時,咱們必須在一個對象內部包含一個指向函數的屬性,並經過這個屬性間接引用函數,從而把this間接綁定到這個對象上。

JavaScript提供的絕大多數函數以及你本身建立的全部函數均可以使用call(…) 和 apply (…) 方法。

這兩個方法的第一參數是一個對象,是給this準備的,接着在調用函數時將其綁定到this。由於你能夠直接指定this的綁定對象,所以咱們稱之爲顯式綁定。

function fod() {
        console.log(this.a);
    }
    var fodObj = {
        a: 2
    }
    fod.call(fodObj); //2

經過fod.call(…)方法,能夠強制把this綁定到fodObj這個對象上。

然而,顯示綁定仍然沒法解決以前提出的丟失綁定問題。

可是顯示綁定的一個變種能夠解決這個問題。

function foh() {
        console.log(this.a);
    }
    var fohObj = {
        a: 2
    }
    var baa = function() {
        foh.call(fohObj);
    }
    baa(); //2
    setTimeout(baa, 100); //2
    baa.call(window); //2

咱們建立了一個baa函數,並在它的內部手動調用了foh.call(fohObj),所以強制把foh的this綁定到了fohObj上。不管以後如何調用函數baa,它總會手動在fohObj上調用foh。這種綁定是一種顯示的強制綁定,所以咱們稱之爲硬綁定。

硬綁定的典型應用場景就是建立一個包裹函數,負責接收參數並返回值。

function foi(something) {
        console.log(this.a, something);
        return this.a + something;
    }
    var foiObj = {
        a: 2
    }
    var bae = function() {
        return foi.apply(foiObj, arguments);
    }
    var b = bae(3);
    console.log(b);

另外一種使用方法是建立一個能夠重複使用的輔助函數。

function fol(something) {
        console.log(this.a, something);
        return this.a + something;
    }

    function bind(fn, obj) {
        return function() {
            return fn.apply(obj, arguments);
        }
    }
    var folObj = {
        a: 3
    }
    var bac = bind(fol, folObj);
    var c = bac(4);
    console.log(c);

4 new綁定

使用new來調用函數,或者說發生構造函數調用時,會自動執行下面的操做。

1) 建立(或者說構造)一個全新的對象。
2) 這個新對象會被執行Prototype鏈接。
3) 這個新對象會綁定到函數調用的this。
4) 若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象。

function abc(a) {
        this.a = a;
    }
    var x = new abc(10);
    console.log(x.a);
相關文章
相關標籤/搜索