最近在拜讀《你不知道的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在任何情況下都不指向函數的詞法作用域
詞法作用域:定義在詞法階段的作用域,即由你在寫代碼時將變量和塊作用域寫在哪裏來決定