掌握 JavaScript 中的 this 關鍵字:不再回頭
JavaScript 的 this 關鍵字是一個基本概念,經常讓初學者和經驗豐富的開發人員感到困惑。如果不徹底理解它的動態性質,可能會導致意想不到的行為。本綜合指南旨在揭開這一點的神秘面紗,探索其各種背景、細微差別和最佳實踐,並附有說明性示例和具有挑戰性的問題,以鞏固您的理解。
對此的介紹
在 JavaScript 中,this 是一個關鍵字,引用目前程式碼正在執行的物件。與其他一些靜態綁定 this 的程式語言不同,JavaScript 的 this 是根據函數的呼叫方式動態決定的。
1. 全球背景
當不在任何函數內部時,this 指的是全域物件。
- 在瀏覽器中:全域物件是window。
- 在 Node.js 中: 全域物件是全域的。
範例
console.log(this === window); // true (in browser) console.log(this === global); // true (in Node.js)
注意: 在嚴格模式下('use strict';),全域上下文中的 this 仍然是全域物件。
2. 函數上下文
一、常規功能
在常規函數中,這是由函數的呼叫方式決定的。
- 預設綁定:如果在沒有任何上下文的情況下呼叫函數,則 this 會引用全域物件(或在嚴格模式下未定義)。
範例:
function showThis() { console.log(this); } showThis(); // Window object (in browser) or global (in Node.js)
- 隱式綁定:當函數作為物件的方法被呼叫時,this 引用該物件。
範例
const person = { name: 'Alice', greet: function() { console.log(`Hello, I'm ${this.name}`); } }; person.greet(); // "Hello, I'm Alice"
我們可以使用 call、apply 或 bind 明確設定它。
function greet() { console.log(`Hello, I'm ${this.name}`); } const person = { name: 'Bob' }; greet.call(person); // "Hello, I'm Bob"
二.箭頭功能
箭頭函數有一個詞法 this,這表示它們在創建時從周圍的作用域繼承了 this。
範例
const person = { name: 'Charlie', greet: () => { console.log(`Hello, I'm ${this.name}`); } }; person.greet(); // "Hello, I'm undefined" (or global name if defined)
說明:由於箭頭函數沒有自己的this,所以this指的是全域對象,而不是person對象。
箭頭函數的正確用法:
const person = { name: 'Dana', greet: function() { const inner = () => { console.log(`Hello, I'm ${this.name}`); }; inner(); } }; person.greet(); // "Hello, I'm Dana"
具有挑戰性的方面:如果將方法分配給變數並調用,則可能會失去其預期的上下文。
範例
const calculator = { value: 0, add: function(num) { this.value += num; return this.value; } }; console.log(calculator.add(5)); // 5 console.log(calculator.add(10)); // 15 const addFunction = calculator.add; console.log(addFunction(5)); // NaN (in non-strict mode, this.value is undefined + 5)
3.建構函數和this
當函數使用 new 關鍵字作為建構子時,this 指的是新建立的實例。
console.log(this === window); // true (in browser) console.log(this === global); // true (in Node.js)
重要提示:
• 如果不使用new,this 可能會引用全域物件或在嚴格模式下未定義。
• 建構函數通常將第一個字母大寫,以區別於常規函數。
4. 事件處理程序中的 this
在事件處理程序中,這是指接收事件的元素。
範例
function showThis() { console.log(this); } showThis(); // Window object (in browser) or global (in Node.js)
5. 使用call、apply和bind進行明確綁定
JavaScript 提供了明確設定 this 值的方法:
- call: 呼叫函數,並將其設定為第一個參數,後面跟著函數參數。
const person = { name: 'Alice', greet: function() { console.log(`Hello, I'm ${this.name}`); } }; person.greet(); // "Hello, I'm Alice"
- apply: 與 call 類似,但接受陣列形式的參數。
function greet() { console.log(`Hello, I'm ${this.name}`); } const person = { name: 'Bob' }; greet.call(person); // "Hello, I'm Bob"
- bind: 傳回一個新函數,並將 this 綁定到第一個參數。
const person = { name: 'Charlie', greet: () => { console.log(`Hello, I'm ${this.name}`); } }; person.greet(); // "Hello, I'm undefined" (or global name if defined)
用例:
- 從其他物件借用方法。
- 確保這在回調中保持一致。
6. 課堂上的 this
ES6 引入了類別,它為建構函數和方法提供了更清晰的語法。在類別方法中,this 指的是實例。
範例:
const person = { name: 'Dana', greet: function() { const inner = () => { console.log(`Hello, I'm ${this.name}`); }; inner(); } }; person.greet(); // "Hello, I'm Dana"
類別中的箭頭函數:
箭頭函數可用於從類別上下文繼承 this 的方法,這對於回呼很有用。
const calculator = { value: 0, add: function(num) { this.value += num; return this.value; } }; console.log(calculator.add(5)); // 5 console.log(calculator.add(10)); // 15 const addFunction = calculator.add; console.log(addFunction(5)); // NaN (in non-strict mode, this.value is undefined + 5)
常見陷阱和最佳實踐
I. 失去這個背景
將方法作為回調時,原始上下文可能會遺失。
問題
function Person(name) { this.name = name; } const alice = new Person('Alice'); console.log(alice.name); // "Alice"
解
使用綁定來保留上下文。
<button> <p><strong>Arrow Function Caveat:</strong><br> Using arrow functions in event handlers can lead to this referring to the surrounding scope instead of the event target.<br> <strong>Example:</strong><br> </p> <pre class="brush:php;toolbar:false">button.addEventListener('click', () => { console.log(this); // Global object or enclosing scope });
二.錯誤使用箭頭函數
箭頭函數沒有自己的 this,這在用作方法時可能會導致意外的行為。
問題
function greet(greeting) { console.log(`${greeting}, I'm ${this.name}`); } const person = { name: 'Eve' }; greet.call(person, 'Hello'); // "Hello, I'm Eve"
解
使用常規函數作為物件方法。
greet.apply(person, ['Hi']); // "Hi, I'm Eve"
三.避免全局 this
無意中設定全域物件的屬性可能會導致錯誤。
問題
const boundGreet = greet.bind(person); boundGreet('Hey'); // "Hey, I'm Eve"
解
使用嚴格模式或正確的綁定。
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a noise.`); } } const dog = new Animal('Dog'); dog.speak(); // "Dog makes a noise."
先進理念
一、巢狀函數中的this
在巢狀函數中,this 可能不引用外部的 this。解決方案包括使用箭頭函數或將其儲存在變數中。
箭頭函數範例:
class Person { constructor(name) { this.name = name; } greet = () => { console.log(`Hello, I'm ${this.name}`); } } const john = new Person('John'); john.greet(); // "Hello, I'm John"
變數範例:
const obj = { name: 'Object', getName: function() { return this.name; } }; const getName = obj.getName; console.log(getName()); // undefined or global name
二.這與原型
使用原型時,這是指實例。
console.log(this === window); // true (in browser) console.log(this === global); // true (in Node.js)
結論
JavaScript 中的 this 關鍵字是一個多功能且強大的功能,如果正確理解,可以大大增強您的編碼能力。
解決這個問題的 10 個棘手問題
為了真正鞏固您對此的理解,請解決以下具有挑戰性的問題。每個問題都旨在測試 JavaScript 中 this 關鍵字的不同面向和邊緣情況。解決方案在最後。
問題一:神秘的輸出
function showThis() { console.log(this); } showThis(); // Window object (in browser) or global (in Node.js)
問題 2:箭頭函數驚喜
const person = { name: 'Alice', greet: function() { console.log(`Hello, I'm ${this.name}`); } }; person.greet(); // "Hello, I'm Alice"
問題 3:在回呼中綁定 this
function greet() { console.log(`Hello, I'm ${this.name}`); } const person = { name: 'Bob' }; greet.call(person); // "Hello, I'm Bob"
問題4:正確使用bind
const person = { name: 'Charlie', greet: () => { console.log(`Hello, I'm ${this.name}`); } }; person.greet(); // "Hello, I'm undefined" (or global name if defined)
問題5:建構函數中的this
const person = { name: 'Dana', greet: function() { const inner = () => { console.log(`Hello, I'm ${this.name}`); }; inner(); } }; person.greet(); // "Hello, I'm Dana"
問題 6:事件處理程序上下文
const calculator = { value: 0, add: function(num) { this.value += num; return this.value; } }; console.log(calculator.add(5)); // 5 console.log(calculator.add(10)); // 15 const addFunction = calculator.add; console.log(addFunction(5)); // NaN (in non-strict mode, this.value is undefined + 5)
問題 8:Promise 中的 this
function Person(name) { this.name = name; } const alice = new Person('Alice'); console.log(alice.name); // "Alice"
問題 9:用 bind 鏈接
<button> <p><strong>Arrow Function Caveat:</strong><br> Using arrow functions in event handlers can lead to this referring to the surrounding scope instead of the event target.<br> <strong>Example:</strong><br> </p> <pre class="brush:php;toolbar:false">button.addEventListener('click', () => { console.log(this); // Global object or enclosing scope });
問題 10:類別和繼承
function greet(greeting) { console.log(`${greeting}, I'm ${this.name}`); } const person = { name: 'Eve' }; greet.call(person, 'Hello'); // "Hello, I'm Eve"
棘手問題的解決方案
問題1的解決方案:
當 getName 被指派給變數並在沒有任何物件上下文的情況下呼叫時,這預設為全域物件。在非嚴格模式下,this.name指的是全域名稱,即'Global'。在嚴格模式下,這將是未定義的,從而導致錯誤。
greet.apply(person, ['Hi']); // "Hi, I'm Eve"
問題2的解決方案:
箭頭函數沒有自己的this;他們從周圍的範圍繼承它。在這種情況下,周圍的範圍是全域上下文,其中 this.name 是 'Global'。
const boundGreet = greet.bind(person); boundGreet('Hey'); // "Hey, I'm Eve"
問題3的解決方案:
在 setInterval 回呼中,this 指的是全域物件(或在嚴格模式下未定義)。因此, this.seconds 要么增加 window.seconds 要么在嚴格模式下拋出錯誤。 timer.seconds 仍為 0。
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a noise.`); } } const dog = new Animal('Dog'); dog.speak(); // "Dog makes a noise."
問題4的解決方案:
將retrieveX綁定到模組後,呼叫boundGetX()可以正確地將其設定為模組。
class Person { constructor(name) { this.name = name; } greet = () => { console.log(`Hello, I'm ${this.name}`); } } const john = new Person('John'); john.greet(); // "Hello, I'm John"
問題5的解決方案:
箭頭函數 getModel 從建構子繼承 this,它引用新建立的汽車實例。
const obj = { name: 'Object', getName: function() { return this.name; } }; const getName = obj.getName; console.log(getName()); // undefined or global name
問題6的解決方案:
在使用常規函數的事件處理程序中,this 指的是接收事件的 DOM 元素,即按鈕。由於按鈕沒有 name 屬性,因此 this.name 未定義。
console.log(this === window); // true (in browser) console.log(this === global); // true (in Node.js)
問題7的解決方案:
- 第一個console.log(this.name);裡面的outerFunc引用了obj,所以它印出'Outer'。
- 第二個console.log(this.name);內部innerFunc引用全域對象,因此它在嚴格模式下列印'Global'或undefined。
function showThis() { console.log(this); } showThis(); // Window object (in browser) or global (in Node.js)
問題8的解決方案:
在 Promise 建構子中,this 指的是全域物件(或在嚴格模式下未定義)。因此, this.value 是未定義的(或在嚴格模式下導致錯誤)。
const person = { name: 'Alice', greet: function() { console.log(`Hello, I'm ${this.name}`); } }; person.greet(); // "Hello, I'm Alice"
問題9的解決方案:
乘法函數與第一個參數 a 綁定為 2。當呼叫 double(5) 時,它有效地計算乘法(2, 5)。
function greet() { console.log(`Hello, I'm ${this.name}`); } const person = { name: 'Bob' }; greet.call(person); // "Hello, I'm Bob"
問題10的解決方案:
在Dog類別的speak方法中,setTimeout回呼是一個常規函數。因此,回呼中的 this 指的是全域對象,而不是狗實例。 this.name 為“未定義”,或如果未全域定義名稱,則會導致錯誤。
const person = { name: 'Charlie', greet: () => { console.log(`Hello, I'm ${this.name}`); } }; person.greet(); // "Hello, I'm undefined" (or global name if defined)
要解決此問題,請使用箭頭函數:
const person = { name: 'Dana', greet: function() { const inner = () => { console.log(`Hello, I'm ${this.name}`); }; inner(); } }; person.greet(); // "Hello, I'm Dana"
現在,它正確記錄了:
const calculator = { value: 0, add: function(num) { this.value += num; return this.value; } }; console.log(calculator.add(5)); // 5 console.log(calculator.add(10)); // 15 const addFunction = calculator.add; console.log(addFunction(5)); // NaN (in non-strict mode, this.value is undefined + 5)
以上是掌握 JavaScript 中的 this 關鍵字:不再回頭的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。
