有在寫JS的朋友,應該或多或少都會有一些感覺,就是覺得JS非常好上手,也知道自己在寫什麼。但是寫起來就是不踏實...
這是為什麼呢?
因為JS的語言特性跟其他語言不大相同,來舉個例子看看
1=="1"
如果這是在C#,會直接報錯,因為型別不同。
而在JS之中呢,卻會發現沒有錯耶!執行也正常耶!那就這樣寫好了> O <
然後事隔多年,悲劇就發生了
相信部分朋友也跟我一樣,對JS抱有憧憬,卻又害怕受傷害。
因為現在JS正夯,不論是NG、Vue、React,甚至是D3、桌面應用程式、APP,都可以透過JS達成,既然如此,能說JS不重要嗎?
當然重要!那有認真的啃完犀牛書過嗎?
當然沒有
也因此造就了對JS沒把握,寫起來不踏實的感覺,不了解語言的特性,自然而然就寫不出漂亮的程式碼~
於是乎,報名了保哥的Javascript核心實戰課程,也就有了這篇文章的產生。
一、基礎型別
Javascript只有二種型別:原始型別、物件型別
原始型別:數值、字串、布林、undefined、null
物件型別:除了上述這五種之外的型別,皆為物件,因此,陣列、函式皆可以認定是物件型別。
擴充型別:其實這個型別不能跟原始型別、物件型別相提並論,而是該放在物件型別底下。擴充型別是類似Date這種型別,本身不是原始型別,而是透過物件型別擴充的型別,算是JS本身建立來便利使用的型別,骨子裡還是屬於物件。
那原始型別跟物件型別有什麼差別呢?差在記憶體之中宣告的位置
原始型別:直接存在記憶體位址之中的型別,
物件型別:物件會在記憶體之中建立一個位址,而物件中的屬性會另外再建立一份記憶體位置。因此存放的記憶體位址數量是N+1,N是物件中的屬性,1是物件。
再來說說原始型別的undefined、null,undefined屬於未經賦值的型別,就是已經宣告,但裡面空無一物,是JS中預設無資料的型別。
那null呢?除非主動給予null值,不然非常少情況值會是null,所以就別太糾結了XD
希望這樣的說明在多年以後還看得懂在講什麼鬼東西...
二、資料參照以及var的特性
變數及屬性差異
由於JS的特性為,非原始型別皆為物件,因此連Window都是物件型別,而物件型別可以任意增加、刪除屬性。
那屬性又是什麼,跟變數又有什麼差別?
在Windows底下,輸入這二行時,會有些微的差異
a = "我是屬性";
var a="我是變數";
屬性:僅存在於物件底下,可以被刪除。
變數:在開發時期宣告,編譯時期檢查。並且無法被刪除。
通常JS的汙染即是這二個東西在搞鬼,仍需要特別注意的是,在JS的Window中,如果先a再var a,則a則會從屬性轉為變數,導致a的屬性無法被刪除。
2.var的特性
這東西哪有什麼生命週期可言,有夠邪惡亂七八糟的
好啦...var有幾個特性,務必要注意以及小心使用
- 生命週期:如果宣告在函式之中,有效範圍僅存在於函式中,聽不懂的話認function的大括號就對了!因此不會有宣告二個同名的var變數衝突問題,但這種寫法應該要避免才對。
- Hoisting:由於javascript會在程式碼執行之前,先進行一次編譯,編譯什麼呢?就是把使用var的宣告都移至第一行,為了避免錯誤的判斷,建議把var都提升到第一行會比較好!
- 資料重複性:由於var本身是一個宣告,因此在非同步之中,迴圈中跑var i的話,如果迴圈先跑完,此時會導致i的值為最後一次迴圈的值,當非同步函式開始執行時,會導致印出來的資料非預期。如迴圈印i的答案不是1~9,而是999999999。解決方式改用let即可。
三、記憶體的配置方式及優化方式
這邊要先講的變數,變數是JS中執行時期宣告出來的,這筆資料紀錄的並非資料本身,而是記憶體存放位址。
詳細的就看程式碼吧!如果看不懂,建議打一行,再跟著看一次會比較清楚!
// 1.在記憶體位址中建立 1 的資料
// 2.宣告a的變數,資料 為1的記憶體位址
// 如果第二點看不懂,可以這樣想像
// 是指向門牌(位址),而不是門牌內住的人(值)
var a = 1;
// 3.建立b的變數,並且取得跟a一樣的記憶體位址
var b = a;
// 4.在記憶體中再建立 3 的資料
// 5.清除a原本指向1的記憶體位址的路徑
// 注意!
// 1依舊存在,只是a的記憶體位址發生改變
// 而b的記憶體位址依舊指向1
// 6.a重新指向 3 的記憶體位址
a = 3;
// 印出來會是3
console.log(a);
// 印出來會是1
console.log(b);
這個概念懂了就大概能理解資料是怎麼變化的,再來就是要注意JS的資料生命週期了,例如物件中屬性、變數,以及函式中的屬性、變數。
接著我們來看看如何釋放記憶體資源
1.delete 屬性名稱
2.清除該筆資料與根物件的連結
第二點會比較難懂,直接舉例會比較好懂一些。
根物件:Window
子物件:A
子物件的屬性:B,型別為物件
因此我們可以知道,若是要取得B,要這樣輸入Window.A.B對吧。但如果我直接把A給刪除掉呢?
此時由於B的屬性依附在A之中,是而B透過A跟Window做聯繫的,當A消失了,B自然而然也就跟Window斷了聯繫,此時會被認定為無效的物件,進而釋放該筆資源。
如果上面懂了就再來談談IIFE吧!
IIFE又名立即函式,主要是將函式轉變為物件來執行,由於執行完畢後,並未將值存下,因此函式中所宣告的資源會立即被釋放。
有看到宣告被加粗嗎?這是說如果在函式中直接使用屬性處理值的話,會直接汙染到WIindow的屬性,也就失去IIFE原先的美意了。
如果看不懂也沒關係!謹記立即函式中愛用宣告即可!
來看個範例吧!
// 透過第一行的左括號,以及最後一行的右括號
// 將函式轉變成一個物件,物件中的資料即是函式
(function (a,b){
return a + b;
})(2,4);
// 再最後一行則直接呼叫 物件中的函式,因此可以傳入二個參數
// 這個範例的重點就是左右括號將函式轉為物件
// 並且執行完畢後會立即釋放資源,可以有效管理記憶體
歡迎您的加入,讓這個社群更加美好!