【YDKJS讀書筆記】關於Js中的this Part1

書讀到"this & object prototype"這一卷。

章節到了「this All Makes Sense Now!」

書裏面開篇就提到,this並不複雜,只不過被不少程序員加了本身的臆想以訛傳訛,說到底,仍是基礎知識不熟悉。
的確,看過不少技術文章,分析this都有那種管中窺豹的感受就是着重在舉例,論述這個現象,而不求甚解。
閒言少敘,開始總結。程序員

首先說一個重要的技術名詞,call-site,咱們平時在debug的時候,可能會接觸到callstack這個詞,感受上其實有那麼一點相似。面試

我理解:
call-stack:是一連串的方法執行的鏈式結果
call-site:只是上一個調用當前方法上下文環境,能夠理解爲context。app

好比下面這個片斷:oop

function callfirst(){
    // call-stack:callfirst
    // call-site:全局
    callsecond();
}
function callsecond(){
    // call-stack:callfirst -> callsecond
    // call-site:callfirst
}
callfirst();

爲何說到這個call-site,肯定this對象其實就是找到call-site的過程。this

說到這裏,還要再提一個細節,js中的this,不是面向對象中傳統的概念,這個this不是放在function中就是這個function的context,也不是任何場合都表明了整個js運行環境中的context,這個this,你就能夠理解爲是剛纔提到的call-site,必須是有依據的context。prototype

下面就總結一下找到call-site的方法,也就是如何正確找到並使用this。(規則我就直接使用原文的副標題)debug

1.Default Binding:看看以下代碼片斷

function foo() {
    console.log( this.a );
}

var a = 2;

foo();

這裏出現this的地方,是foo方法裏,咱們先肯定call-site,顯而易見,foo方法的call-site就是最後一行foo,隸屬於全局對象,那麼這個this就呼之欲出了,這個this就表明這個代碼的做用域,而this.a訪問的也就是var a = 2;這條語句賦值的屬性,因此控制檯會打印出一個2。
貼一句書中的原文:code

called with a plain, un-decorated function reference.對象

就是說在代碼中很簡單,沒有任何修飾的調用,this就能夠理解爲全局的做用域對象。
可是這種規則,不適用於strict mode環境下的js代碼,若是用在strict mode中,以上代碼須要改寫成爲ci

function foo() {
    console.log( this.a );
}

var a = 2;

(function(){
    "use strict";

    foo(); // 2
})();

2.Implicit Binding:仍是看代碼

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2,
    foo: foo
};

obj.foo();

這種直譯爲隱式綁定的方法,肯定call-site的方法就是看是由誰調用的方法,在上面這個例子中,再直白以及明顯不過了,obj中有一個foo屬性,綁定的foo方法,那麼此時foo方法中的call-site就是obj,obj中有一個屬性是a,因此代碼會輸出2

變形:

function foo() {
    console.log( this.a );
}

var obj2 = {
    a: 42,
    foo: foo
};

var obj1 = {
    a: 2,
    obj2: obj2
};

obj1.obj2.foo(); // 42

若是遇到這種鏈式的風格,就本着就近原則,離foo方法最近的obj2就是foo的call-site,方法中this.a的值就是obj2中a的值。

在Implicit Binding的狀況下會有一種叫Implicitly Lost的狀況發生,簡單直白點說就是剛纔那種鏈式調用的方式,被隱藏在了各類其餘的狀況之下,舉例來講明。

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2,
    foo: foo
};

var bar = obj.foo;

var a = "oops, global";

bar();

上面這種狀況將obj.foo賦值給了bar, 按照慣性思惟,看到obj.foo第一反應,我以爲this應該等價於obj,然而事實卻哐哐打臉,
上面代碼的foo的call-site是bar,雖然鏈式複製了一大堆給了bar,但實際上,bar在這個時點是等價於foo的,因此這個方法的call-site就是bar,那麼this.a的值就是全局屬性的a,與obj就不相干了。
與上面狀況相同的還有以下幾種變種狀況:

function foo() {
    console.log( this.a );
}

function doFoo(fn) {
    fn();
}

var obj = {
    a: 2,
    foo: foo
};

var a = "oops, global"; 

doFoo( obj.foo );

刨除一切感官上的理解,最終調起foo方法的是fn()這句話,fn的值雖然是由obj.foo傳過來的,但其實這種狀況與上面說到的方式徹底是等價的
解析一下,fn()就是foo的call-site,而根據第一個default binding原則,fn前面是乾淨沒有任何修飾符的,因此foo中的this表明的就是全局對象。

這裏須要強調的就是,鏈式方法不管是賦值仍是做爲方法的參數,不能被長長的語句迷惑雙眼,照準call-site是理順思路的一切法門。

這種鏈式賦值,this指向問題在js中叫作fall back to default binding。

3.Explicit Binding

說到這個顯示綁定,就得提到兩個方法,一個叫作call,另外一個叫apply,在如今這個時點,咱們暫且理解幾個點,這兩個方法,是全部function對象均可以調用的內建方法(涉及到prototype),他們的第一個參數,咱們就能夠理解爲this對象,這是一種強制把this注入到方法中的一種手段。舉個例子

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2
};

foo.call( obj );

仍是熟悉的味道,可是咱們卻換了配方,咱們不直接調用foo方法,而是使用foo中的call方法,把obj傳到call中做爲foo中的this對象,控制檯會爲咱們輸出一個2,call能夠換成apply。

foo.apply(obj);

在這種傳一個參數做爲this對象的功能方面,call與apply是等價的。

4 new Binding

這個恐怕也是不少使用js的朋友們最容易混淆的地方,new在js中生成的只是一個function,new過是在一個function前面搶了一個new單詞,而這樣表示會讓function有一些新的變化
大致上有4點:

  1. 產生一個新的function對象

  2. 這個與原型鏈有關,暫且不說

  3. new出來的function對象調用的方法是使用的this,就是它自己

  4. 除對象自己改變本身自己之外,每次new出來的對象都是全新的對象(這話我再潤色一下)

上例子:

function foo(a) {
    this.a = a;
}

var bar = new foo( 2 );
console.log( bar.a );

這個比之以前的複雜狀況就太淺顯了,望文生義便可。

看了以上文章對於解決this面試題應該會有不小的幫助。

相關文章
相關標籤/搜索