1. 程式人生 > >You Don't Know JS: this & Object Prototypes( 第一章 this or That?)

You Don't Know JS: this & Object Prototypes( 第一章 this or That?)

time github type 不是函數 read trac 作用 spa any

Foreword

this 關鍵字和prototypes

他們是用JS編程的基礎。沒有他們創建復雜的JS程序是不可能的。

我敢說大量的web developers從沒有建立過JS Object,僅僅對待這門語言作為一個事件綁定膠水,在按鈕和Ajax請求之間。

我也曾是他們的一員,但是當我了解到如何掌握prototypes並創建JS對象,一個世界的可能性對我打開了。


  • Chapter 1: this Or That?
  • Chapter 2: this All Makes Sense Now!
  • Chapter 3: Objects
  • Chapter 4: Mixing (Up) "Class" Objects
  • Chapter 5: Prototypes
  • Chapter 6: Behavior Delegation
  • Appendix A: ES6 class
  • Appendix B: Thank You‘s!

Chapter 1: this Or That?

this作為一個識別符號關鍵字自動定義在每個函數的作用域中,但是它究竟涉及什麽,甚至困擾著有經驗的JS developer。


Why this?

既然很難理解,為什麽還使用?它的麻煩大於它的價值嗎?

看案例:

function identify() {
    return this.name.toUpperCase();
}

var me = { name: "Kyle" };

var you = {
	name: "Reader"
}; 
identify.call( me ); // KYLE
identify.call( you ); // READER

這段代碼讓identify函數被多次調用。

如果不用this關鍵字,也可以通過傳入一個context對象來實現:

function identify(context) {
    return context.name.toUpperCase();
}
identify( you ); // READER

由此可見,this機制提供了更優雅的方式,暗示傳入了一個對象,這讓API設計更清晰,復用更容易。


Confusions

首先,消除一些錯誤觀念。常常有2個錯誤的假設:

Itself

假設this是函數自身。

function foo(num) {
    console.log( "foo: " + num );

    // keep track of how many times `foo` is called
    this.count++;
}

foo.count = 0;

var i;

for (i=0; i<10; i++) {
    if (i > 5) {
        foo( i );
    }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9

// how many times was `foo` called?
console.log( foo.count ); // 0 -- WTF?
因為this指向了window對象。this指向的是調用函數的對象。

//在call方法內加上函數對象的identifier名字
for (var i=0; i<10; i++) {
    if (i > 5) {
        foo.call( foo, i );
    }
}

// how many times was `foo` called?
console.log( foo.count ); // 4

除此之外,還有規避this的方法。但都沒有直面this到底是什麽的問題。

  • 使用foo.count代替this.count.
  • 單獨聲明一個data對象{ count: 0},用於計數 data.count++

Its Scope

另一個常見的誤區是關於this,它涉及了函數的作用域。這裏有些詭計:

在某些情況下是對的,但在其他情況下,這麽理解是受到誤導的!

this關鍵字,在任何時候,都不涉及一個function的 Lexical scope

function foo() {
    var a = 2;
    this.bar();
}

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

foo(); //undefined

寫 這段代碼的人,試圖通過this關鍵字,把變量a傳遞給bar函數。以為this是橋梁,能在兩個函數的lexical scope之間傳遞內部變量a。 ?。沒有這個橋梁!!

this不能得到foo函數的Lexical scope!!

this和Lexical scope是不相關的!!!


What‘s this?

this機制到底是如何工作的?

this和函數調用的方式相關!

當一個函數被invoked,一條記錄被創建。這條記錄包含信息:

  • 函數從哪裏調用(the call-stack)
  • 函數如何被invoked
  • 什麽參數被傳入。

這條記錄的屬性之一就是 this 引用。它會在函數的執行期間被使用。

下一章,我們會學習尋找一個函數的call-site(調用點),來決定它的執行如何綁定this關鍵字

Review

this既不是函數自身的引用也不是函數的lexical作用域的引用!!

this實際是一個在函數被invoked時創建的綁定binding!!!

this引用什麽由函數被調用的地點call-site決定!!!

You Don't Know JS: this & Object Prototypes( 第一章 this or That?)