關於this

最近在拜讀《你不知道的js》,而此篇是對於《你不知道的js》中this部分的筆記整理,希望能有效的梳理,並且鞏固關於this的知識點

一、this是一種怎樣的機制

1、this是在運行時進行綁定的,並非在編寫時綁定,它的上下文取決於函數調用時的各種條件,它指向什麼完全取決於函數的調用位置;

2、this的綁定與函數申明位置無關係,只取決於函數的調用方式;

3、當函數被調用時,會創建一個活動記錄,即執行上下文。這包括函數在哪被調用、函數調用方式、傳入的參數信息等。this則爲其記錄的一個屬性,會在函數執行的過程中用到;

二、this的作用

首先看一段代碼:

function identify() {
    return this.name.toUpperCase();
}
function speak() {
    var greeting = "Hello, I'm " + identify.call(this);
    console.log(greeting);
}
var me = {
    name: "Mike"
};
var you = {
  name: "Jenny"  
};
identify.call(me); // Mike
identify.call(you); // Jenny

speak.call(me); // Hello, I'm Mike
speak.call(you); // Hello, I'm Jenny

這段代碼可在不同的上下文對象中重複使用identify(),speak();

若不使用this,可將代碼變換如下:

function identify(con) {
    return con.name.toUpperCase();
}
function speak(con) {
    var greeting = "Hello, I'm " + identify.call(con);
    console.log(greeting);
}
var me = {
    name: "Mike"
};
var you = {
  name: "Jenny"  
};
identify(me); // Mike
speak(you); // Hello, I'm Jenny

this可以以一種更優雅的方式來傳遞上下文對象,使代碼變得簡潔且易於複用

三、對於this的誤解

誤解一、this指向函數自身(僅從字面上理解this的含義)

先來看一段代碼:

function foo(num) {
    console.log("foo: " + num);
    // 記錄foo被調用的次數
    this.count++;
}
foo.count = 0;
var i;
for(i = 0; i < 0; i++) {
    if(i > 5) {
        foo(i);
    }
}
// foo(6);
// foo(7);
// foo(8);
// foo(9);

// foo被調用了幾次?
console.log(foo.count); // 0

爲什麼此處的foo.count值爲0呢?

執行foo.count時,的確向函數對象foo添加一個屬性count,但函數內部代碼的this並非指向foo,而this.count是創建了一個全局變量,所以this指向了window

此時this.count的值爲NaN

若要實現foo.count = 4 ,則將代碼變化如下:

方法一、

function foo(num) {
    console.log("foo: " + num);
    // 記錄foo被調用的次數
    data.count++;
}
var data = {
    count: 0
};
var i;
for(i = 0; i < 0; i++) {
    if(i > 5) {
        foo(i);
    }
}
// foo(6);
// foo(7);
// foo(8);
// foo(9);

// foo被調用了幾次?
console.log(data.count); // 4

此寫法雖然達到了理想的狀態,但卻忽略了this

方法二、

function foo(num) {
    console.log("foo: " + num);
    // 記錄foo被調用的次數
    foo.count++;
}
foo.count = 0;
var i;
for(i = 0; i < 0; i++) {
    if(i > 5) {
        foo(i);
    }
}
// foo(6);
// foo(7);
// foo(8);
// foo(9);

// foo被調用了幾次?
console.log(data.count); // 4

這種寫法同樣迴避了this的問題

方法三、

function foo(num) {
    console.log("foo: " + num);
    // 記錄foo被調用的次數
    this.count++;
}
foo.count = 0;
var i;
for(i = 0; i < 0; i++) {
    if(i > 5) {
        // 使用call(...)可以確保this指向函數對象foo本身
        foo.call(foo, i);
    }
}
// foo(6);
// foo(7);
// foo(8);
// foo(9);

// foo被調用了幾次?
console.log(data.count); // 4

此方法使用call,改變了上下文對象,強制將this指向foo函數對象

總結:通過以上例子說明,僅僅從字面意思來解釋this是不準確的,而是要通過關注執行上下文,以及函數的調用位置來判斷this的指向

誤解二、this指向函數的作用域

這個問題在某些情況下是正確的,但也有錯誤的情況,例:

function foo() {
    var a = 2;
    this.bar();
}
function bar() {
    console.log(this.a);
}
foo(); // RefrenceError: a is not defined

這段代碼試圖使用this聯通foo()、bar()的詞法作用域,從而讓bar()可以訪問foo()作用域中的變量。但this不可能在詞法作用域中查到什麼。因爲this指向全局,而a屬於foo作用域中的變量,所以無法查到

總結:this在任何情況下都不指向函數的詞法作用域

詞法作用域:定義在詞法階段的作用域,即由你在寫代碼時將變量和塊作用域寫在哪裏來決定

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章