網站一口酥 001 : JavaScript 的 “this”

繼續閱讀以前,筆者期待您已經了解 什麼是 JavaScript,以及為何它對當代網站開發如此重要。

作為設計師 / 社群行銷 / 寫手(Medium) 自由接案,與另一位服務過多間企業的資深前端 Moojing 是朋友,我希望詳實紀錄自己作為在不同技能中不斷心態歸零的新手,在學習網站與 JavaScript 時的過程。

既然有圖像與寫作相關背景,也想善用相關能力跟資源,期待扮演好學習者中舉著火炬 (torch) 的嚮導型學徒,也呼應 Moojing 在 《JavaScript概念三明治》 (改寫自第11屆鐵人邦 JavaScript 原力覺醒) 中善用圖像、闡述微進階概念的跨領域思路,降低對程式語言的恐懼。該書的食物命名恰巧也是由我剛好想到推薦,好讓這些網站語言概念更好入口,因此,作為新進者,本單元名稱就命名為「網站一口酥」,或許不只有 JavaScript,也包含一些後端語言、環境架設等等,搭配圖像跟闡述,給大家比文件再生動一點點的體驗;但每篇力求輕薄短小,故命名「網站一口酥」!由於尚嫌生嫩,內容不免有錯,歡迎各方大大評論或提出各種想法,也讓我們與各位萌新在路上一起成長⁽⁽٩(๑˃̶͈̀ ᗨ ˂̶͈́)۶⁾⁾

關於 this… 為你打開一扇窗!

隨意在瀏覽器的開發者工具中,輸入 “this” 之後會有什麼呢?
結果 this 指向全域 (Global) 物件 “window”,這並不意外。

Window {window: Window, self: Window, document: document, name: '', location: Location, …}
在終端機中,通常要使用 console.log(this) 並會多回傳 (return) 一個 undefined

但若將 this 放在一個函式 (function) 中——在 JavaScript 中也意謂著一個特殊的物件 (object) ——事情就有點有趣了。

function inside(){
console.log(this);
}
inside();
undefined 是回傳值 (return),可忽略

結果也是 window 物件!怎麼不是 inside() 這個 function 物件?(JavaScript 中除了 Primitive type 原始型別 (數字、字串、布林) 以外所有東西都是物件,函式也是一特殊物件)
第一次接觸 “this ” 的朋友想必會如此覺得。其實,我們都被 this 的文法給迷惑了。即使是中文,他也是意謂著「這個」,當然會認定為自身 (itself),但其實在程式語言中,語言文法上的認定,不必然等於實際的運作邏輯,否則數學人看到「 x = x + 1 」豈不氣死?

this 在函式中,會是什麼?

如果我們再繼續增加階層呢?這次我們在 inside 函式外加上 globalTown 函式:答案是什麼呢?

function globalTown(){
function inside(){
console.log(this);
}
}
globalTown();

如果繼續加上階層呢?在 inside 函式裡頭加上 smokeBar 函式,變成三層。

竟然還是 window 物件!我的天啊,為什麼?
因為在函式中,this 會指向全域物件

Function 中呼叫 this

一個可能的聯想是,在函式 (function) 的世界觀中,所有人都是來自 Global Town,無論住在裡頭的哪一區域、哪一棟建築物中、乃至房間,你都是全域鎮的人。A 圓跟 B 圓在 Smoke 酒吧相遇了,A 圓問 B 圓「你來自哪裡?」B 圓回答:「這裡。」A 圓預設理解為,「喔,全域鎮 (global town) 的朋友。」

this 在物件中,會是什麼?

如果是在一般物件中,this 又會如何呢?

var hotel = {
name : 'Volcano City',
breakfast : true,
smokeBar : function(){
console.log(this);
}
};
hotel.smokeBar();
回傳的物件就是 hotel 物件

這時候的 this,就是指多層 (cascading) 大括號外的環境,以這個案例來說,就是 hotel 這個物件本身。
所有的方法 (method),都在 hotel 物件裡頭了。而且會在上一個階層,即便在屬性 (attribute) 裏頭的方法也ㄧ樣。

可能的比喻探索:Smoke 酒吧在一間飯店裡頭有分店。這次 C 圓跟 D 圓,在該飯店的附設酒吧相遇了。C 圓問:「你住在哪裡?」D 圓回答:「這裡。」,這裡,預設指的是這間飯店,Volcano City Hotel 火山城市旅館。

那如果再多層一點呢?

將 smokeBar 從方法改成一個型別為物件的屬性 bar,並在裡頭放入一個 name 屬性,以及一個方法,並在該方法裡頭印出 this,會是如何呢?

var hotel = {
name : 'Volcano City',
breakfast : true,
bar : {
name : 'Smoke',
talk : function(){
console.log(this);
}
}
};
hotel.bar.talk();
回傳的物件則是 bar 物件

回傳的 this 則是 bar 物件(作為 hotel 的一個方法),且 this.name 為 “smoke”

速記表

稍等一下!

如果將取值出來的「物件中的方法」,也就是在物件中的函式,重新賦值為全域變數,會發生什麼事呢?(記得結尾不能用 () 呼叫!)

var bar = hotel.bar.talk;

接著,我們來看看結果吧!(這次要呼叫這個已經變成函式的變數 bar!)

console.log(bar());  // 或直接 bar();

你猜到了嗎?
因為將物件中的方法取值出來,重新賦予給一個全域變數,使得該變數變成函式,從該函式呼叫,就重新變成 global 啦!

頭昏了嗎?聽首歌吧!

本日音樂推薦

Macroblank — 痛みの永遠 — YouTube

下次我們一起來聊聊這次函式、物件中的 “Implicit”, “Explicit” binding 內隱 /顯性綁定,以及new 建構函式、箭頭函式,一舉把 this 攻破吧!

持續努力的 GitHub

轉型中的個人社群列表

部落格即將架設,Stay Tuned!

--

--

Murmurline Spirit (Steward Tsou) 星際特調
網站實力三明治

你療癒、硬頸的文字好夥伴:星際廢話線、星際區塊線 @murmurline 🚀 FB fanpage 行銷、設計、網站開發基礎與區塊鏈 Writer / Designer / Marketing / Web3&Blockchain / NFT ✨FB🚀@murmursteward