JavaScript中this綁定規(guī)則你理解了嗎
前言
JavaScript 中的 this 是一個非常重要的概念,也是一個令新手開發(fā)者甚至有些不深入理解的多年經(jīng)驗開發(fā)者都會感到困惑的概念。
如果你希望自己能夠使用 this 編寫更好的代碼或者更好理解他人的代碼,那就跟著我一起理解一下this吧。
要理解this的原因
我們先搞清楚為什么要理解 this 的,再去學(xué)習(xí)它。
學(xué)習(xí) this 可以幫助我們更好地理解代碼的上下文和執(zhí)行環(huán)境,從而編寫更好的代碼。
例1:
function speakfullName(){
console.log(this.firstname + this.lastname)
}
var firstname = '南'
var lastname = '墨'
const gril = {
firstname: '黎',
lastname: '蘇蘇',
speakfullName,
}
const boy = {
firstname: '澹臺',
lastname: '燼',
speakfullName,
}
gril.speakfullName(); // 輸出: 黎蘇蘇
boy.speakfullName(); // 輸出: 澹臺燼
speakfullName(); // 輸出: 南墨在這個例子中,如果你沒理解 this 的用法,那么閱讀這段代碼就會覺得奇怪,為什么同一個函數(shù)會輸出不同的結(jié)果。之所以奇怪,是因為你不知道他的上下文到底是什么。
學(xué)習(xí) this 可以幫助我們編寫更具可重用性和可維護性的代碼
在例1中可以在不同的上下文中使用 this,不用針對不同版本寫不同的函數(shù)。當(dāng)然不使用 this,也是可以的。
例2:
function speakfullName(person){
console.log(person.firstname + person.lastname)
}
const gril = {
firstname: '黎',
lastname: '蘇蘇',
}
const boy = {
firstname: '澹臺',
lastname: '燼',
}
speakfullName(gril); // 黎蘇蘇
speakfullName(boy); // 澹臺燼雖然目前這段代碼沒有問題,如果后續(xù)使用的模式越來越復(fù)雜,那么這樣的顯示傳遞會讓代碼變得難以維護和重用,而this的隱式傳遞會顯得更加優(yōu)雅一些。因此,學(xué)習(xí)this可以幫助我們編寫更具有可重用性和可維護性的代碼。
接下來我們開始正式全面解析 this
解析 this
我相信大家多多少少都理解一些 this 的用法,但可能不夠全面,所以接下來我們就全面性的理解 this。
很多人可能認(rèn)為this 寫在哪里就是指向所在位置本身,如下代碼:
var a = 2
function foo (){
var a = 1
console.log(this.a)
}
foo();有些人認(rèn)為會輸出1,實際是輸出2,這就是不夠理解 this 所產(chǎn)生的的誤會。
那this的機制到底是什么樣的呢?
其實this不是寫在哪里就被綁定在哪里,而是代碼運行的時候才被綁定的。也就是說如果一個函數(shù)中存在this,那么this到底被綁定成什么取決于這個函數(shù)以什么樣的方式被調(diào)用。
既然已經(jīng)提出了這樣一個機制,那我們該如何根據(jù)這個機制,去理解和判斷this被綁定成什么呢?
下面我們繼續(xù)介紹這個機制的基本原理。
調(diào)用位置
上面說過,函數(shù)的調(diào)用位置會影響this被綁定成什么了,所以我們需要知道函數(shù)在哪里被調(diào)用了。
我們回頭去看一下 例1,來理解什么是調(diào)用位置:
// ... gril.speakfullName(); // 輸出: 黎蘇蘇 boy.speakfullName(); // 輸出: 澹臺燼 speakfullName(); // 輸出: 南墨
同一個函數(shù) speakfullName, 在不同的調(diào)用位置,它的輸出就不一樣。
在 gril 對象中調(diào)用時,輸出了黎蘇蘇,在 boy 對象中調(diào)用時,輸出了澹臺燼,在全局調(diào)用時輸出了南墨。
當(dāng)然例子中的調(diào)用位置是非常容易看出來的。所以我們接下來繼續(xù)講解在套多層的情況下如何找到調(diào)用位置。
我們要找到調(diào)用位置就要分析調(diào)用棧。
看下簡單例子:
function baz() {
// 當(dāng)前調(diào)用棧:baz
console.log('baz')
bar(); // bar 調(diào)用的位置
}
function bar() {
// 當(dāng)前調(diào)用棧:baz-bar
console.log('bar')
foo(); // foo 調(diào)用的位置
}
function foo() {
// 當(dāng)前調(diào)用棧:baz-bar-foo
console.log('foo')
}
baz() // baz的調(diào)用位置其實調(diào)用棧就是調(diào)用位置的鏈條,就像上面代碼例子中所分析的一樣。不過在一些復(fù)雜點的代碼中,這樣去分析很容易出錯。所以我們可以用現(xiàn)代瀏覽器的開發(fā)者工具幫助我們分析。
比如上例中,我們想找到 foo 的調(diào)用位置,在 foo 中第一行輸入debugger。
// ...
function foo() {
debugger
// ...
}
// ...或者打開瀏覽器的開發(fā)者工具到源代碼一欄找到,foo的代碼的第一行打一個斷點也行,如下圖:

接著在源代碼一欄,找到調(diào)用堆棧的foo的下一個就是bar,bar就是foo的調(diào)用位置。

綁定規(guī)則
接下來看看調(diào)用位置如何決定this被綁定成什么,并且進行總結(jié)。
默認(rèn)規(guī)則
第一種情況是函數(shù)最經(jīng)常被調(diào)用的方式,函數(shù)被單獨調(diào)用??匆韵吕樱?/p>
var name = '澹臺燼'
function fn(){
console.log('我是' + this.name)
}
fn() // 我是澹臺燼運行fn后,最終輸出了 我是澹臺燼。眾所周知,上例中的 name 是全局的變量,這樣說明了fn中的 this.name 被綁定成了全局變量name。因此,this指向了全局對象。
因為在上例的代碼片段中,foo的調(diào)用位置是在全局中調(diào)用的,沒有其他任何修飾, 所以我們稱之為默認(rèn)規(guī)則。
使用了嚴(yán)格模式的話,上例代碼會出現(xiàn)什么樣的情況呢?
var name = '澹臺燼'
function sayName(){
"use strict"
console.log(this) // (1)
console.log('我是' + this.name) // (2)
}
fn()
// undefined
// TypeError: cannot read properties of undefined (reading 'name') as sayName可以看出來(1)也就是this,輸出了undefiend 所以(2)就直接報錯了。
因此我們可以得出默認(rèn)規(guī)則的結(jié)論:在非嚴(yán)格模式下,this默認(rèn)綁定成全局對象,在嚴(yán)格模式下,this 被綁成 undefined。
隱式綁定
這條規(guī)則需要我們?nèi)ヅ袛嗪瘮?shù)的調(diào)用是否有上下文對象,也就是說函數(shù)調(diào)用的時候前面是否跟了一個對象,舉個例子看下。
function sayName() {
console.log(`我是` + this.name)
}
var person = {
name: '澹臺燼',
sayName,
}
person.sayName(); // 我是澹臺燼在這個例子中, sayName 前面有一個 person,也就是說 sayName 函數(shù)有一個上下文對象person, 這樣調(diào)用 sayName 的時候,函數(shù)中 this 被綁定成了person,因此 this.name 和 person.name 是一樣的。
在觀察隱式綁定的時候,有兩種值得我們注意的情況:
如果說一個函數(shù)是通過對象的方式調(diào)用時,只有最后一個對象起到上下文的作用。 例3:
function sayName() {
console.log(`我是` + this.name)
}
var child = {
name: '澹臺燼',
sayName,
}
var father = {
name: '澹臺無極',
child,
}
father.child.sayName(); // 我是澹臺燼這個例子中,是通過一個對象鏈調(diào)了sayName,沒有輸出我是澹臺無極,而是我是澹臺燼。因此 this 指向了child 對象,說明this 最終綁定為對象鏈的最后一個對象。
隱式丟失的情況就是被隱式綁定的函數(shù)丟失綁定的上下文,轉(zhuǎn)而變成了應(yīng)用默認(rèn)綁定。
function sayName() {
console.log(`我是` + this.name)
}
var person = {
name: '澹臺燼',
sayName,
}
var personSayName = person.sayName;
var name = '南墨'
pesonSayName() // '我是南墨'雖然 personSayName 看起來是由 person.sayName 賦值的來,擁有上下文對象person,但實際上 personSayName 被賦予的是 sayName 函數(shù)本身,因此此時的 personSayName其實是一個不帶修飾的函數(shù), 所以說會被認(rèn)為是默認(rèn)綁定。
顯示綁定
隱式綁定是通過一個看起來不經(jīng)意間的上下文的形式去綁定的。
那也當(dāng)然也有通過一個明顯的上下文強制綁定的,這就是顯示綁定
在 javaScript 中,要是使用顯示綁定,就要通過 call 和 apply方法去強制綁定上下文了
這兩個方法的使用方法是什么樣的呢? call 和 apply的第一個參數(shù)的是一樣的,就是傳入一個我們想要給函數(shù)綁定的上下文。
來看一下下面的例子
function sayName () {
console.log(this.name)
}
var person = {
name: 南墨
}
sayName.call(person) // 南墨看到?jīng)]? 我們通過call的方式,將函數(shù)的上下文綁定為了 person,因此打印出了 南墨。
使用了 call 綁定也會有綁定丟失的情況,所以我們需要一種保證在我意料之內(nèi)的辦法, 可以改造顯示綁定,思考如下代碼:
function sayName() {
console.log(this.name)
}
var person = {
name: 南墨
}
function sayNanMo() {
sayName.call(person);
}
sayNanMo() // 南墨
setTimeout(sayNanMo, 10000) // 南墨
sayNanMo.call(window) // 南墨這樣一來,不管怎么操作,都會輸出南墨,是我想要的結(jié)果
我們將 sayName.call(person) 放在的 sayNanMo 中,因此sayName 只能被綁定為我們想要綁定的 person。
我們可以將其寫成可復(fù)用的函數(shù)
function bind(fn, obj) {
return function() {
fn.apply(obj, arguments)
}
}ES5 就提供了內(nèi)置的方法 Function.prototype.bind,用法如下:
function sayName() {
console.log(this.name)
}
var person = {
name: 南墨
}
sayName.bind(person)new綁定
new 綁定也可以影響函數(shù)調(diào)用時的 this 綁定行為,我們稱之為new 綁定。
思考如下代碼:
function person(name) {
this.name = name;
this.sayName = function() {
console.log(this.name)
}
}
var personOne = new person('南墨')
personOne.sayName() // 南墨personOne.sayName 能夠輸出南墨,是因為使用 new 調(diào)用 person 時,會創(chuàng)建一個新的對象并將它綁定到 person 中的 this 上,所以personOne.sayName中的 this.name 等于外面的this.name。
規(guī)則之外
值得一提的是,ES6的箭頭函數(shù),它的this無法使用以上四個規(guī)則,而是而是根據(jù)外層(函數(shù)或者全局)作用域來決定this。
function sayName () {
return () => {
console.log(this.name)
}
}
var person1 = {
name: 南墨
}
var person2 = {
name: '澹臺燼'
}
sayName.call(person1)
sayName.call(person1).call(person2) // 澹臺燼,如果是普通函數(shù)會輸南墨
}總結(jié)
要想判斷一個運行的函數(shù)中this的綁定,首先要找到函數(shù)調(diào)用位置,因為它會影響this的綁定。然后使用四個綁定規(guī)則:new綁定、顯示綁定、隱式綁定、默認(rèn)規(guī)則 來判斷this的綁定。
到此這篇關(guān)于JavaScript中this綁定規(guī)則你理解了嗎的文章就介紹到這了,更多相關(guān)JavaScript this綁定規(guī)則內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 一文全面解析JS中的this綁定規(guī)則
- 細說JavaScript中的this指向與綁定規(guī)則
- JavaScript this綁定與this指向問題的解析
- JavaScript?中的?this?綁定規(guī)則詳解
- JavaScript中this的綁定你知道幾種?
- 詳解JavaScript中的this硬綁定
- 一文搞懂JavaScript中的this綁定規(guī)則
- JavaScript中?this?的綁定指向規(guī)則
- 詳解JavaScript的this指向和綁定
- JavaScript this綁定過程深入詳解
- React.js綁定this的5種方法(小結(jié))
- JavaScript調(diào)用模式與this關(guān)鍵字綁定的關(guān)系
- 深入理解JavaScript this綁定規(guī)則
相關(guān)文章
javascript中的Function.prototye.bind
這篇文章主要介紹了javascript中的Function.prototye.bind的相關(guān)資料,需要的朋友可以參考下2015-06-06
用javascript實現(xiàn)檢測指定目錄是否存在的方法
今天看到一篇關(guān)于onegreen被掛馬的代碼發(fā)現(xiàn)這個函數(shù),它用js就可以檢測,制定的目錄或指定的文件是否存在,一般用來讀chm文件中的圖片來檢測,目錄的存在。高手就是不學(xué)好。2008-01-01
在javaScript中檢測數(shù)據(jù)類型的幾種方式小結(jié)
在用javaScript編程的過程中,我們經(jīng)常會遇到這樣一個問題,就是需要檢測一個數(shù)據(jù)或變量的類型,本篇文章主要介紹了在javaScript中檢測數(shù)據(jù)類型的幾種方式小結(jié),有興趣的可以了解一下。2017-03-03
js實現(xiàn)輪播圖效果 z-index實現(xiàn)輪播圖
這篇文章主要為大家詳細介紹了js實現(xiàn)輪播圖效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-01-01

