在實際應用中,了解
this
的行為是非常重要的,特別是在編寫庫或框架時,或者當你需要在回調函數中訪問特定的上下文時,通常推薦使用箭頭函數或者其他方法來確保this
的正確指向。
在ES6中,this
的值取決于它是如何被調用的。
this
不是一個函數或對象,而是一個特殊的關鍵字,其值在函數被調用時確定。
以下是在不同場景中 this
的值的概述:
01 全局作用域中的this
在JavaScript中,全局作用域指的是在代碼的任何位置都可以訪問的、變量和函數的范圍。
具體可以這么理解:當你在腳本的頂層(不在任何函數或代碼塊內部)聲明一個變量或函數時,它就在全局作用域中。
在全局作用域中,this
指向全局對象。
在瀏覽器環境中,全局對象是 window
;
在 Node.js 環境中,全局對象是 global
。
下面是一個在瀏覽器環境中演示這一點的簡單例子:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script>"use strict"// 在全局作用域中console.log(this === window); // 應該輸出 true,表示 this 指向 window 對象console.log(this); // 輸出window對象// 定義一個全局變量var globalVar = "I am a global variable";// 使用 this 訪問全局變量console.log(this.globalVar); // 輸出 "I am a global variable"// 使用 window 訪問全局變量console.log(window.globalVar); // 輸出 "I am a global variable"// 定義一個函數,并在全局作用域中調用它function testFunction() {console.log(this); // 在瀏覽器環境中,輸出 window 對象,"use strict"下輸出undefined}testFunction();</script>
</body>
</html>
在這個例子中,this
在全局作用域中指向 window
對象。我們通過比較 this
和 window
來驗證這一點,并且使用 this
和 window
來訪問一個全局變量 globalVar
,以證明它們都可以用來訪問全局作用域中的變量。
如果你在 Node.js 環境中運行代碼,全局對象將是 global
,你可以類似地測試:
// 在 Node.js 的全局作用域中
console.log(this === global); // 應該輸出 true,表示 this 指向 global 對象// 定義一個全局變量
global.globalVar = "I am a global variable";// 使用 this 訪問全局變量
console.log(this.globalVar); // 輸出 "I am a global variable"// 使用 global 訪問全局變量
console.log(global.globalVar); // 輸出 "I am a global variable"// 定義一個函數,并在全局作用域中調用它
function testFunction() {console.log(this); // 在 Node.js 環境中,輸出 global 對象
}testFunction();
在這個 Node.js 例子中,this
在全局作用域中指向 global
對象,并且我們同樣使用 this
和 global
來訪問一個全局變量 globalVar
。
02 函數調用中的this
當一個函數不是作為對象的方法調用時(也就是說,它是獨立調用的,或者作為回調函數等被調用),this
的值在非嚴格模式下默認為全局對象(在瀏覽器中通常是 window
),而在嚴格模式下它是 undefined
。
下面我將給出兩個示例程序來演示這個行為。
首先是非嚴格模式下的示例:
// 非嚴格模式
function exampleFunction() {console.log(this); // 在非嚴格模式下,this 指向 window 對象console.log(this.globalVar); // => I am a global variable
}exampleFunction();// 你可以通過檢查 window 對象來驗證這一點
function setGlobalVar() {window.globalVar = "I am a global variable";
}
setGlobalVar();
exampleFunction(); // 輸出包含 globalVar 的 window 對象
在這個例子中,exampleFunction
不是作為任何對象下的方法調用的,因此在非嚴格模式下,this
指向 window
對象。
我們在 setGlobalVar
函數中設置了一個全局變量 globalVar
,然后在 exampleFunction
中通過 this
訪問它,證明了 this
確實指向 window
。
03 對象方法中的this
當一個函數作為對象的方法被調用時,this
關鍵字指向調用該方法的對象。下面是一個簡單的示例來展示這個行為:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>03-對象方法中的this</title>
</head>
<body><script>// 定義一個對象,它有一個方法叫做 greetconst person = {name: "Alice",getName: function () {console.log(this.name);}};// 調用 person 對象的 getName方法person.getName(); // 輸出 "Alice"// getName中this指向person對象,因為getName是作為 person 的方法被調用的</script>
</body>
</html>
在這個例子中,getName
函數是 person
對象的一個方法。當我們通過 person.getName()
調用這個方法時,this
關鍵字在函數內部指向了 person
對象。因此,this.name
訪問的是 person
對象的 name
屬性,并輸出了相應的信息。
04構造函數中的this
當一個函數被用作構造函數,并使用 new
關鍵字調用時,this
關鍵字會指向新創建的對象實例。下面是一個簡單的例子來說明這個行為:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>04-構造函數中的this</title>
</head>
<body><script>// 定義一個構造函數 function Person(name, age) {this.name = name;this.age = age;// 可以添加一個方法來訪問實例屬性 this.greet = function () {console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);};}// 使用 new 關鍵字來調用構造函數,創建一個新的對象實例 const alice = new Person('Alice', 25);alice.greet(); // 輸出 "Hello, my name is Alice and I'm 25 years old." // 驗證 this 指向的是 alice 對象 console.log(alice.name); // 輸出 "Alice" console.log(alice.age); // 輸出 25 // 創建一個新的對象實例 const bob = new Person('Bob', 30);bob.greet(); // 輸出 "Hello, my name is Bob and I'm 30 years old." // 驗證 this 指向的是 bob 對象 console.log(bob.name); // 輸出 "Bob" console.log(bob.age); // 輸出 30</script>
</body>
</html>
每次使用 new
關鍵字調用 Person
構造函數時,都會創建一個新的對象實例,并且 this
都會指向那個新對象。這就是為什么 alice
和 bob
有各自獨立的 name
和 age
屬性,以及它們各自的 greet
方法。
在這個例子中,Person
函數被用作構造函數,因為我們使用 new
關鍵字來調用它。當構造函數被調用時,JavaScript 會創建一個新的空對象,并將這個新對象的內部鏈接([[Prototype]]
)設置為構造函數的 prototype
對象。然后,構造函數中的代碼執行,其中 this
關鍵字引用新創建的對象。
因此,當我們設置 this.name
和 this.age
時,我們實際上是在新創建的對象上設置屬性。同樣,this.greet
方法也是添加到新對象上的一個方法。
05箭頭函數中的this
箭頭函數在 JavaScript 中是一個非常重要的特性,它提供了一種更簡潔的函數書寫方式,并且它不綁定自己的 this
,而是繼承自包圍它的函數或全局作用域的 this
。
這意味著在箭頭函數內部,this
的值是在定義箭頭函數時確定的,而不是在調用時確定的。
下面是一個簡單的示例來說明這個行為:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>05-箭頭函數中的this</title>
</head>
<body><script>function OuterFunction() {this.value = 42;// 這是一個普通函數 function innerFunction() {console.log(this.value); // 這里的 this 指向 OuterFunction 的實例 }// 這是一個箭頭函數 const arrowFunction = () => {console.log(this.value); // 這里的 this 繼承自 OuterFunction 的實例 };// 調用普通函數和箭頭函數 innerFunction(); // 輸出 42 arrowFunction(); // 輸出 42 }const obj = new OuterFunction();// 當我們在外部調用 innerFunction 時,this 指向全局對象(在瀏覽器中通常是 window) // 因此,以下代碼會拋出一個錯誤,因為 this.value 是 undefined obj.innerFunction(); // Uncaught TypeError: Cannot read property 'value' of undefined // 但是,箭頭函數不會改變它的 this 上下文 // 因此,即使我們在外部調用 arrowFunction,它仍然可以訪問 OuterFunction 的 this 上下文 obj.arrowFunction(); // 輸出 42</script>
</body>
</html>
在OuterFunction
中定義了一個普通函數 innerFunction
和一個箭頭函數 arrowFunction
。
當 OuterFunction
被調用時,它創建了一個新的對象實例,并且 this
在 innerFunction
和 arrowFunction
中都指向這個新創建的對象。
當我們直接調用 obj.innerFunction()
時,this
指向了全局對象(在瀏覽器中是 window
),因此 this.value
是 undefined
,導致拋出一個錯誤。
然而,當我們調用 obj.arrowFunction()
時,即使我們是在外部調用的,arrowFunction
內部的 this
仍然指向 OuterFunction
的實例,因此可以正確地訪問 value
屬性。這是因為箭頭函數不綁定自己的 this
,而是從定義它的上下文中繼承 this
。
TODO 嵌套函數
全局函數中的嵌套函數
對象方法中的嵌套函數
總結
場景 | this 的值 | 描述 |
---|---|---|
全局作用域 | window (瀏覽器) 或 global (Node.js) | 在全局作用域中,this 指向全局對象。 |
函數調用 | undefined (嚴格模式) 或 window (非嚴格模式) | 當一個函數不是作為對象的方法調用時,this 的值在非嚴格模式下是 window ,在嚴格模式下是 undefined 。 |
對象方法 | 調用該方法的對象 | 當一個函數作為對象的方法被調用時,this 指向調用該方法的對象。 |
構造函數 | 新創建的對象 | 當一個函數作為構造函數使用 new 關鍵字調用時,this 指向新創建的對象。 |
箭頭函數 | 定義時的上下文 | 箭頭函數不綁定自己的 this ,它繼承自包圍它的函數或全局作用域的 this 。 |
事件處理器 | 調用事件處理器的對象 | 在DOM事件處理器中,this 通常指向觸發事件的元素。 |
定時器函數 | undefined (嚴格模式) 或 window (非嚴格模式) | 在 setTimeout 或 setInterval 的回調函數中,this 的值在非嚴格模式下是 window ,在嚴格模式下是 undefined 。 |
Call, Apply, Bind | 指定的對象 | 使用 call 、apply 或 bind 方法可以顯式地設置 this 的值。 |
請注意,箭頭函數的行為略有不同,因為它們不綁定自己的 this
。相反,它們從定義它們的上下文繼承 this
。
希望這個表格能幫助你理解ES6中 this
的不同行為!
在JavaScript中,特別是在ES6及其之后的版本中,回調函數中使用普通函數和箭頭函數時,this
的值可能會有所不同。這主要取決于回調函數的調用方式和上下文。以下是普通函數和箭頭函數在回調中作為方法使用時 this
的區別:
普通函數
當回調函數是一個普通函數時,this
的值通常取決于該函數如何被調用。如果該函數是作為對象的方法被調用,那么 this
將指向調用該方法的對象。如果該函數是作為回調函數被調用,并且沒有使用 call
、apply
或 bind
方法來顯式地設置 this
的值,那么 this
可能會指向全局對象(在瀏覽器中是 window
),或者在沒有嚴格模式的情況下是 undefined
。
例如:
function Example() {this.value = 5;setTimeout(function() {console.log(this.value); // this 指向全局對象或undefined(嚴格模式)}, 1000);
}const example = new Example(); // 輸出可能是 undefined,取決于環境
箭頭函數
箭頭函數不綁定自己的 this
,它繼承自包圍它的函數或全局作用域的 this
。這意味著在箭頭函數內部,this
的值將始終與包圍它的外部函數的 this
保持一致。
因此,當回調函數是箭頭函數時,this
將保持外部函數的 this
值,即使回調函數以不同的方式被調用。
例如:
function Example() {this.value = 5;setTimeout(() => {console.log(this.value); // this 繼承自Example函數的this,所以輸出5}, 1000);
}const example = new Example(); // 輸出 5
在這個例子中,即使 setTimeout
是一個全局函數,并且通常會導致回調函數中的 this
指向全局對象,但由于使用了箭頭函數,this
仍然保持了 Example
函數的上下文。
總結:在回調函數中,普通函數的 this
值可能會根據回調函數的調用方式而改變,而箭頭函數的 this
值則始終與其外部函數的 this
保持一致。因此,在需要確保 this
的值始終為特定上下文時,使用箭頭函數通常是更安全的選擇。
this不是常量, 它在程序中的不同地方會求值為不同的值。
this是面向對象編程中使用的關鍵字。在方法體中,this求值為調用方法的對象。
this
在 標準函數 和 箭頭函數 中有不同的行為。
1、標準函數中的 this
window.color = 'red';
// 全局上下文中調用函數時,this 指向 windows
function sayColor() { console.log(this.color);
}
sayColor(); // 'red' let o = { color: 'blue'
};
o.sayColor = sayColor;
o.sayColor(); // 'blue'
2、嵌套函數
嵌套函數普通函數內部的this是全局對象,嵌套箭頭函數的this是外層對象的this
注意:vue中相反:vue中嵌套普通函數內部的this是vue,而嵌套箭頭函數的this是全局對象
let o = {m:function(){let self = this;this === o;console.log('o.m() this:',this);f(); function f(){this === o;self ===o;console.log('o.m.f() this:',this);}const f2 = ()=>{console.log('o.m.f2() this:',this);}f2();const f3 = (function(){console.log('o.m.f3() this:',this);}).bind(this);f3();}
}
o.m();
console.log(o);
2、箭頭函數中的this
window.color = 'red';
let sayColor = () => console.log(this.color);
sayColor(); // 'red' let o = { color: 'blue'
};
o.sayColor = sayColor;
o.sayColor(); // 'red'