關於 this 你想知道的一切都在這裡
無論在 javascript 的日常使用中還是前端面試過程中,this 的出鏡率都極高。這無疑說明了,this 的重要性。但 this 非常靈活,導致許多人覺得 this 的行為難以理解。本文從為什麼要有 this 作為切入點,總結了 this 的六大規則,希望能幫助你解答困惑。
簡介
this 實際上相當於一個參數,這個參數可能是開發中手動傳入的,也可能是 JS 或第三方傳入的。
這個參數,通常指向的是函數執行時的「擁有者」。 this 的機制,可以讓函數設計的更簡潔,而且多用性更好。
this 是在函數執行時進行綁定的,綁定規則一共六條,分別是:
●new 綁定:使用 new 關鍵字建立物件時,this 會綁定到已建立的物件上。
●明確綁定:使用 call、apply 或 bind 方法明確綁定時, this 為其第一個參數。
●隱式綁定:當函數掛在物件上執行時,系統會隱含地將 this 綁定到該物件上。
●預設綁定:當函數獨立執行時,嚴格模式 this 的預設綁定值為 undefined,否則為全域物件。
●箭頭函數綁定:使用箭頭函數時,this的綁定值等於其外層的普通函數(或全域物件本身)的this。
●系統或第三方綁定:當函數作為參數,傳入系統或第三方提供的介面時,傳入函數中的 this 是由系統或第三方綁定的。
this 的作用
this 的機制提供了一個優雅的方式,隱式地傳遞一個對象,這可以讓函數設計的更加簡潔,並且復用性更好。
考慮下面一個例子,有兩個按鈕,點擊後將其背景改為紅色。
function changeBackgroundColor(ele) { ele.style.backgroundColor = 'red'; } btn1.addEventListener('click',function () { changeBackgroundColor(btn1); }); btn2.addEventListener('click',function () { changeBackgroundColor(btn2); });
在這裡,我們明確地將被點擊的元素傳遞給了 changeBackgroundColor 函數。但實際上,這裡可以利用 this 隱式傳遞上下文的特點,直接在函數取得目前被點擊的元素。如下:
function changeBackgroundColor() { this.style.backgroundColor = 'red'; } btn1.addEventListener('click',changeBackgroundColor); btn2.addEventListener('click',changeBackgroundColor);
在第一個例子中,被點擊元素是透過 ele ,這個形式參數來取代的。而在第二個例子中,是透過一個特殊的關鍵字 this 來取代。 this 它的作用和形式參數類似,其本質上是一個物件的引用,它的特殊性在於不需要手動傳值,所以使用起來會更加簡單和方便。
六大規則
在實際使用中, this 究竟指向哪個物件是最令人困惑的。本文歸類了六類情景,總結六條 this 的綁定規則。
1.new 綁定
使用 new 建立物件的時候,類別中的 this 指的是什麼?
class Person { constructor(name){ this.name = name; } getThis(){ return this } } const xiaoMing = new Person("小明"); console.log(xiaoMing.getThis() === xiaoMing); // true console.log(xiaoMing.getThis() === Person); // false console.log(xiaoMing.name === "小明"); // true
在上面範例中,使用了 ES6 的語法建立了 Person 類別。在使用 new 關鍵字建立物件的過程中,this 會由系統自動綁定到已建立的物件上,也就是 xiaoMing。
規則一:在使用 new 關鍵字建立物件時,this 會綁定到已建立的物件上。
2.明確綁定
情景二,使用 call、apply 和 bind 方法,明確綁定 this 參數。
以 call 為例,call 方法的第一個傳入的參數,是 this 所引用的物件。
function foo() { console.log( this === obj ); // true console.log( this.a === 2 ); // true}const obj = { a: 2}; foo.call( obj );
在明確傳遞的情況下,this 指向的物件很明顯,就是 call、apply 或 bind 方法的第一個參數。
規則二:使用 call、apply 或 bind 方法明確綁定時, this 為其第一個參數。
3.隱式綁定
隱式綁定和明確綁定不同的地方在於,明確綁定由開發者來指定this;而隱式綁定時,函數或方法綁定不同的地方在於,明確綁定由開發者來指定this;而隱式綁定時,函數或方法綁定時,函數或方法綁定都會有一個“擁有者”,這個“擁有者”指的是直接呼叫的函數或方法物件。
例一
先看一個最簡單的例子。
function bar() { console.log( this === obj ); }const obj = { foo: function () { console.log( this === obj ); }, bar: bar }; obj.foo(); // trueobj.bar(); // true
函數 foo 是直接掛在物件 obj 裡面的,函數 bar 是在外面定義的,然後掛在物件 obj 上的。無論函數是在何處定義,但最後函數呼叫時,它的「擁有者」是 obj。所以 this 指向的是函數呼叫時的「擁有者」 obj。
例二
為了更深入的理解,再考慮函數重新賦值到新的物件上的情況,來看看下面的例子。
function bar() { console.log( this === obj1 ); // false console.log( this === obj2 ); // true}const obj1 = { foo: function () { console.log( this === obj1 ); // false console.log( this === obj2 ); // true }, bar: bar };const obj2 = { foo: obj1.foo, bar: obj1.bar }; obj2.foo(); obj2.bar();
在這個例子中,將 obj1 中的 foo 和 bar 方法賦值給了 obj2。函數呼叫時,「擁有者」是 obj2,而不是 obj1。所以 this 指向的是 obj2。
例三
物件可以多層嵌套,在這種情況下執行函數,函數的「擁有者」是誰呢?
const obj1 = { obj2: { foo: function foo() { console.log( this === obj1 ); // false console.log( this === obj1.obj2 ); // true } } }; obj1.obj2.foo()
foo 方法/函數中的直接呼叫者是 obj2,而不是 obj1,所以函數的「擁有者」指向的是離它最近的直接呼叫者。
例四
如果一个方法/函数,在它的直接对象上调用执行,又同时执行了 call 方法,那么它是属于隐式绑定还是显式绑定呢?
const obj1 = { a: 1, foo: function () { console.log(this === obj1); // false console.log(this === obj2); // true console.log(this.a === 2); // true } };const obj2 = { a: 2}; obj1.foo.call(obj2); // true
由上,可以看出,如果显式绑定存在,它就不可能属于隐式绑定。
规则三:如果函数是挂在对象上执行的,这个时候系统会隐式的将 this 绑定为函数执行时的“拥有者”。
4.默认绑定
前一小段,讨论了函数作为对象的方法执行时的情况。本小段,要讨论的是,函数独立执行的情况。
在函数直接调用的情况下,this 绑定的行为,称之为默认绑定。
例一
为了简单起见,先讨论在浏览器的非严格模式的下绑定行为。
function foo() { console.log( this === window); // true} foo();
在上面的例子中,系统将 window 默认地绑定到函数的 this 上。
例二
在这里,先介绍一种我们可能会在代码中见到的显式绑定 null 的写法。
function foo() { console.log( this == window ); // true} foo.apply(null);
将例一默认绑定的情况,改为了显式绑定 null 的情况。
在实际开发中,我们可能会用到 apply 方法,并在第一个参数传入 null 值,第二个参数传入数组的方式来传递数组类型的参数。这是一种传统的写法,当然现在可以用 ES6 的写法来代替,但是这不在本文的讨论范围内。
在本例最需要关注的是,this 竟然指向的 window 而不是 null。个人测试的结果是,在函数独立调用时,或者显式调用,传入的值为 null 和 undefined 的情况下,会将 window 默认绑定到 this 上。
在函数多次调用,形成了一个调用栈的情况下,默认绑定的规则也是成立的。
例三
接着,探讨下严格模式下,this 的默认绑定的值。
"use strict"; function foo() { console.log( this === undefined ); } foo(); // true foo.call(undefined); // true foo.call(null); // false
在严格模式下,this 的默认绑定的值为 undefined。
规则四:在函数独立执行的情况下,严格模式 this 的默认绑定值为 undefined,否则默认绑定的值为 window。
5.箭头函数绑定
箭头函数实际上,只是一个语法糖,实际上箭头函数中的 this 实际上是其外层函数(或者 window/global 本身)中的 this。
// ES6 function foo() { setTimeout(() => { console.log(this === obj); // true }, 100); } const obj = { a : 1 } foo.call(obj); // ES5 function foo() { var _this = this; setTimeout(function () { console.log(_this === obj); // true }, 100); } var obj = { a : 1 } foo.call(obj);
规则五:使用箭头函数时,this 的绑定值和其外层的普通函数(或者 window/global 本身) this 绑定值相同。
6.系统或第三方绑定
在 JavaScript 中,函数是第一公民,可以将函数以值的方式,传入任何系统或者第三方提供的函数中。现在讨论,最后一种情况。当将函数作为值,传入系统函数或者第三方函数中时,this 究竟是如何绑定的。
我们在文章一开始提到的,两个按钮例子,系统自动将 this 绑定为点击的按钮。
function changeBackgroundColor() { console.log(this === btn1); // true} btn1.addEventListener('click',changeBackgroundColor);
接着测试系统提供的 setTimeout 接口在浏览器和 node 中绑定行为。
// 浏览器 setTimeout(function () { console.log(this === window); // true },0) // node setTimeout(function () { console.log(this === global); // false console.log(this); // Timeout },0)
很神奇的是,setTimeout 在 node 和浏览器中的绑定行为不一致。如果我们将 node 的中的 this 打印出来,会发现它绑定是一个 Timeout 对象。
如果是第三发提供的接口,情况会更加复杂。因为在其内部,会将什么值绑定到传入的函数的 this 上,事先是不知道的,除非查看文档或者源码。
系统或者第三方,在其内部,可能会使用前面的五种规则一种或多种规则,对传入函数的 this 进行绑定。所以,规则六,实际上一条在由前五条规则上衍生出来的规则。
规则六:调用系统或者第三方提供的接口时,传入函数中的 this 是由系统或者第三方绑定的。

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

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

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

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

Dreamweaver CS6
視覺化網頁開發工具

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

熱門話題

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

PHP與Vue:完美搭檔的前端開發利器在當今網路快速發展的時代,前端開發變得愈發重要。隨著使用者對網站和應用的體驗要求越來越高,前端開發人員需要使用更有效率和靈活的工具來創建響應式和互動式的介面。 PHP和Vue.js作為前端開發領域的兩個重要技術,搭配起來可以稱得上是完美的利器。本文將探討PHP和Vue的結合,以及詳細的程式碼範例,幫助讀者更好地理解和應用這兩

在前端開發面試中,常見問題涵蓋廣泛,包括HTML/CSS基礎、JavaScript基礎、框架和函式庫、專案經驗、演算法和資料結構、效能最佳化、跨域請求、前端工程化、設計模式以及新技術和趨勢。面試官的問題旨在評估候選人的技術技能、專案經驗以及對行業趨勢的理解。因此,應試者應充分準備這些方面,以展現自己的能力和專業知識。

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

Django是一個由Python編寫的web應用框架,它強調快速開發和乾淨方法。儘管Django是web框架,但要回答Django是前端還是後端這個問題,需要深入理解前後端的概念。前端是指使用者直接和互動的介面,後端是指伺服器端的程序,他們透過HTTP協定進行資料的互動。在前端和後端分離的情況下,前後端程式可以獨立開發,分別實現業務邏輯和互動效果,資料的交

Go語言作為一種快速、高效的程式語言,在後端開發領域廣受歡迎。然而,很少有人將Go語言與前端開發聯繫起來。事實上,使用Go語言進行前端開發不僅可以提高效率,還能為開發者帶來全新的視野。本文將探討使用Go語言進行前端開發的可能性,並提供具體的程式碼範例,幫助讀者更了解這一領域。在傳統的前端開發中,通常會使用JavaScript、HTML和CSS來建立使用者介面

Django:前端和後端開發都能搞定的神奇框架! Django是一個高效、可擴展的網路應用程式框架。它能夠支援多種Web開發模式,包括MVC和MTV,可以輕鬆地開發出高品質的Web應用程式。 Django不僅支援後端開發,還能夠快速建構出前端的介面,透過模板語言,實現靈活的視圖展示。 Django把前端開發和後端開發融合成了一種無縫的整合,讓開發人員不必專門學習
