換個思路理解Javascript中的this

在網上不少文章都對 Javascript 中的 this 作了詳細的介紹,但大可能是介紹各個綁定方式或調用方式下 this 的指向,因而我想有一個統一的思路來更好理解 this 指向,使你們更好判斷,如下有部份內容不是原理,而是一種解題思路。segmentfault

從call方法開始

call 方法容許切換函數執行的上下文環境(context),即 this 綁定的對象。瀏覽器

大多數介紹 this 的文章中都會把 call 方法放到最後介紹,但此文咱們要把 call 方法放在第一位介紹,並從 call 方法切入來研究 this ,由於 call 函數是顯式綁定 this 的指向,咱們來看看它如何模擬實現(不考慮傳入 nullundefined 和原始值):閉包

Function.prototype.call = function(thisArg) {
    var context = thisArg;
    var arr = [];
    var result;

    context.fn = this;

    for (let i = 1, len = arguments.length; i < len; i++) {
        arr.push('arguments[' + i + ']');
    }

    result = eval("context.fn(" + arr + ")");

    delete context.fn;

    return result;
}

從以上代碼咱們能夠看到,把調用 call 方法的函數做爲第一個參數對象的方法,此時至關於把第一個參數對象做爲函數執行的上下文環境,而 this 是指向函數執行的上下文環境的,所以 this 就指向了第一個參數對象,實現了 call 方法切換函數執行上下文環境的功能。dom

對象方法中的this

在模擬 call 方法的時候,咱們使用了對象方法來改變 this 的指向。調用對象中的方法時,會把對象做爲方法的上下文環境來調用。函數

既然 this 是指向執行函數的上下文環境的,那咱們先來研究一下調用函數時的執行上下文狀況。this

下面我門來看看調用對象方法時執行上下文是如何的:url

var foo = {
    x : 1,
    getX: function(){
        console.log(this.x);
    }
}
foo.getX();

object-method

從上圖中,咱們能夠看出getX方法的調用者的上下文是foo,所以getX方法中的 this 指向調用者上下文foo,轉換成 call 方法爲foo.getX.call(foo)spa

下面咱們把其餘函數的調用方式都按調用對象方法的思路來轉換。prototype

構造函數中的this

function Foo(){
    this.x = 1;
    this.getX = function(){
        console.log(this.x);
    }
}
var foo = new Foo();
foo.getX();

執行 new 若是不考慮原型鏈,只考慮上下文的切換,就至關於先建立一個空的對象,而後把這個空的對象做爲構造函數的上下文,再去執行構造函數,最後返回這個對象。code

var newMethod = function(func){
    var context = {};
    func.call(context);
    return context;
}
function Foo(){
    this.x = 1;
    this.getX = function(){
        console.log(this.x);
    }
}
var foo = newMethod(Foo);
foo.getX();

creater-method

DOM事件處理函數中的this

DOMElement.addEventListener('click', function(){
    console.log(this);
});

把函數綁定到DOM事件時,能夠看成在DOM上增長一個函數方法,當觸發這個事件時調用DOM上對應的事件方法。

DOMElement.clickHandle = function(){
    console.log(this);
}
DOMElement.clickHandle();

domelement-method

普通函數中的this

var x = 1;
function getX(){
    console.log(this.x);
}
getX();

這種狀況下,咱們建立一個虛擬上下文對象,而後普通函數做爲這個虛擬上下文對象的方法調用,此時普通函數中的this就指向了這個虛擬上下文。

那這個虛擬上下文是什麼呢?在非嚴格模式下是全局上下文,瀏覽器裏是 window ,NodeJs裏是 Global ;在嚴格模式下是 undefined

var x = 1;
function getX(){
    console.log(this.x);
}

[viturl context].getX = getX;
[viturl context].getX();

normal-function

閉包中的this

var x = 1;
var foo = {
    x: 2,
    y: 3,
    getXY: function(){
        (function(){
            console.log("x:" + this.x);
            console.log("y:" + this.y); 
        })();
    }
}
foo.getXY();

這段代碼的上下文以下圖:
closure-function1

這裏須要注意的是,咱們再研究函數中的 this 指向時,只須要關注 this 所在的函數是如何調用的, this 所在函數外的函數調用都是浮雲,是不須要關注的。所以在全部的圖示中,咱們只須要關注紅色框中的內容。

所以這段代碼咱們關注的部分只有:

(function(){
    console.log(this.x);
})();

與普通函數調用同樣,建立一個虛擬上下文對象,而後普通函數做爲這個虛擬上下文對象的方法當即調用,匿名函數中的 this 也就指向了這個虛擬上下文。
closure-function2

參數中的this

var x = 1;
var foo = {
    x: 2,
    getX: function(){
        console.log(this.x);
    }
}
setTimeout(foo.getX, 1000);

函數參數是值傳遞的,所以上面代碼等同於如下代碼:

var getX = function(){
    console.log(this.x);
};
setTimeout(getX, 1000);

而後咱們又回到了普通函數調用的問題。

全局中的this

全局中的 this 指向全局的上下文

var x = 1;
console.log(this.x);

global-this

複雜狀況下的this

var x = 1;
var a = {
    x: 2,
    b: function(){
        return function(){
            return function foo(){
                console.log(this.x);
            }        
        }
    }
};

(function(){
    var x = 3;
    a.b()()();
})();

看到上面的狀況是有不少個函數,但咱們只須要關注 this 所在函數的調用方式,首先咱們來簡化一下以下:

var x = 1;
(function(){
    var x = 3;
    var foo = function(){
        console.log(this.x);
    }
    foo();
});

this 所在的函數 foo 是個普通函數,咱們建立一個虛擬上下文對象,而後普通函數做爲這個虛擬上下文對象的方法當即調用。所以這個 this指向了這個虛擬上下文。在非嚴格模式下是全局上下文,瀏覽器裏是 window ,NodeJs裏是 Global ;在嚴格模式下是 undefined

總結

在須要判斷 this 的指向時,咱們能夠安裝這種思路來理解:

  • 判斷 this 在全局中OR函數中,若在全局中則 this 指向全局,若在函數中則只關注這個函數並繼續判斷。

  • 判斷 this 所在函數是否做爲對象方法調用,如果則 this 指向這個對象,不然繼續操做。

  • 建立一個虛擬上下文,並把this所在函數做爲這個虛擬上下文的方法,此時 this 指向這個虛擬上下文。

  • 在非嚴格模式下虛擬上下文是全局上下文,瀏覽器裏是 window ,Node.js裏是 Global ;在嚴格模式下是 undefined

圖示以下:
judge-this

歡迎關注:Leechikit
原文連接:segmentfault.com

到此本文結束,歡迎提問和指正。寫原創文章不易,若本文對你有幫助,請點贊、推薦和關注做者支持。

相關文章
相關標籤/搜索