[JavaScript Note] — 淺談 JavaScript #2

Introduction & 前言

因為前陣子出車禍,這陣子算是痊癒了,已經一個多月沒有運動,但是運動的感覺還是很熟悉。 寫程式也是這樣,底子打穩才是最重要的。廢話不多說,立馬進入第二章。

接下來的每一章,不免俗的都會來一句:

本篇文章一切皆為筆記作用,有任何錯誤及問題,歡迎理性指教及討論,詳情請查閱公開課程說明書。


Summary & 摘要

本系列將會淺談 JavaScript ,希望給未來自己當作筆記以及剛接觸 JavaScript 的夥伴們,甚至能帶點不一樣觀點或您沒聽過的觀點給大家,所以不會太過深入的討論。

關於淺談的主題將會大致分為下列幾項,項目可能會做更動

  1. JavaScript 執行環境及作用域

  2. 運算子、型別與文法

  3. 物件

  4. 關於 This 那些事

再次補充,其中大多做為筆記作用,並非一定絕對,也希望錯誤之處各位高手能高台貴手,必且不吝嗇的進行指教。

本章討論的主題為下列幾項。

  1. JavaScript 的基礎介紹

  2. 關於型別的部分

  3. 型別種類

  4. 動態型別

  5. 運算子

  6. 邏輯運算子


圖片取自 https://scotch.io/bar-talk/5-tips-to-write-better-conditionals-in-javascript

JavaScript 的基礎介紹

在我們看 JavaScript 時很常看見一些運算符號,或是前面一章說的宣告…等等,這邊就要來介紹 JavaScript 的基本兩個式子,分別是 陳述式(statement)表達式(Expression)。

陳述式(statement)-> 命令指定一系列的操作,特點是不會回傳結果。

表達式(Expression)-> 常常和我們看見的運算符號結合再一起,並會回傳運算結果。

前者類似 If..else 或是變數宣告 var let const 或是迴圈 for…of …等等這些都不會回傳一個結果,後者是我們常看到的 10 + 100 或是 20 / 2 ,會回傳一個特定結果的。

前面一章我們除了變數宣告外還提到了很重要的 **函式(function)**,在這邊函式依然也有兩種式子,就是 函數陳述式(又稱 具名函式) 及 函數表達式。其實和前面大同小異,前者是宣告一個 function 名稱,然後給他操作內容;後者是宣告一個變數然後帶入一個 function 最後會回傳一個結果。

1
2
3
4
5
6
7
8
9
10
// 函式陳述式(陳述式不可為匿名)
function sayHello(){
// do something...
};

// 函示表達式(這是匿名函式 給名字就是 具名函式)
// (仔細看和運算子又綁在一起了!!)
var sayHello = function() {
// do something
};

值得一提的小地方是,有時候在 JavaScript 內寫一些 function 後會忘記加上分號,但是程式碼可以順利地跑,有時候又不行,是因為 JavaScriptASI(Automatic Semicolon Insertion) 的機制,會自動幫你補上分號。通常水能載舟亦能覆舟,有時候斷點之後就會造成你的程式碼錯誤。

1
2
3
;(function() {  // 所以知道為什麼有些立即函式剛開始前面要加上分號了吧!
// do something...
})()

關於型別的部分

另外 JavaScript 有個值得一提的地方,因為 JavaScript 是弱型別(註1),所以在做運算上要特別注意,雖然有時候程式會自動幫你轉換型別,但是如果自己搞不清楚現在到底是什麼型別,會讓自己之後來看或是接手的同事們很辛苦的。

*註1:程式語言的分類上分為 強型別 (Strong type) 及 **弱型別(Weak type)**,而 JavaScript 就是後者,前者在變數宣告時必須去定義這個變數的型別,在運算過程通也鮮少會有自動之型態轉換;後者在定義時則不需要特別去定義型別。所以後面更出現了 TypeScript,多了一些規範,改善弱型別的問題。更多關於 強型別 (Strong type) 及 **弱型別(Weak type) *的介紹歡迎至 神Q超人 大大那兒參閱一系列的文章 精彩好文推推推。

型別種類

JavaScript 在型別上在分為 原始型別物件型別,基本上只要記得 原始型別 的基本幾項,其他不在裡面的都是 物件型別。原始型別有下列幾種:

  1. 布林 Boolean

  2. 空 Null(註2)

  3. 未定義 Undefined

  4. 數值 Number

  5. 字串 String

  6. 整數數值 BigInt

  7. Symbol

註2:在使用 typeof(null) 時會回傳 **Object(物件)**,這是 JavaScript 的長久錯誤之一。

動態型別

前面有提過 JavaScript 有創造跟執行階段,當創造階段會先在記憶體內佔一個空間,一直到執行階段賦予變數一個值得時候,變數就會正式有型別,這時候我們就可以使用 typeof() 來確認變數的型別。

1
2
3
4
5
var name;                  // 創造階段
name = 'Rex'; // 執行階段
console.log(typeof(name)); // 打開 F12 查看會出現 String
name = 666;
console.log(typeof(name)); // 打開 F12 查看會出現 Number

雖然 JavaScript是弱型別的關係,有時候程式會自動幫你做轉換,但是這邊會有一顆大地雷在,初學時常常因為這個踩雷踩的滿身包啊;在程式碼轉換時分為 顯性轉換(Explicit conversion) 及 **隱性轉換(Implicit conversion)**,前者就像是上面例子中我們直接再度賦予 name 數字這種轉換,後者就是我們常常會踩到的雷,就像下面例子。

1
2
3
4
5
var number = 666;
var number2 = '222';

number = number + number2;
// 會出現多少呢?

這邊在初學時第一個反應肯定是 888 ,這就中了型別的陷阱,因為 Number 和 String 無法做相加,所以 JavaScript 會自動幫你將數字轉為字串,結果會變為 ‘666222’。但是如果這時候你把 ‘666222’ 的字串乘上3結果又會變回數字,其實這就是弱型別的問題,這時候就善用 typeof 去鑑定吧!

小筆記之型別的相等 ==>

上面提到的型別在做相等判斷時會有 寬鬆相等嚴格相等,這邊跟 弱型別 又有點關係了。我們有說到 JavaScript 在創建時並不強制要求給予型別,所以在判別兩個型別是否一樣的時候透過 寬鬆相等(==)嚴格相等(===) 結果會不一樣,這邊必須特別小心。


運算子

JavaScript 中我們很常看見運算符號,在這邊我們稱為運算子,關於運算子要注意的有兩點分別為 優先性(Precedence)相依性(Associativity),前者在說的就是類似於先乘除後加減的概念,就是所謂的 權重,權重越重,就會優先執行,後者在說的就是權重相同時,執行的順序

1
2
3
4
5
6
7
8
var a = 2 * 2 + 2 * 3;      // 乘號權重較大(14) 優先執行乘法 然後加法(13)
=> 10

var b = 2 * ( 2 + 2 ) * 3; // 想讓加法先執行 就再加上一個權重較高的括號(20)
=> 24

var c = 10 / ( 2 + 2 ) * 3; // 權重相同時(14) 相依性會由左至右
=> 7.5

邏輯運算子

邏輯運算指雖然只有三項,但是再接觸 JavaScript 卻是很常用到,分別為 &&(且)||(或)!(not) 三項,詳細介紹可以點我

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var number = 123;
var number2 = '666';

// 如果 number 嚴格相等為 數值 ''且'' number2 嚴格相等為 數值 就執行
if(number === Number && number2 === Number) {
// do something...
};

// 如果 number 嚴格相等為 數值 ''或'' number2 嚴格相等為 數值 其中一個為 true 就執行
if(number === Number && number2 === Number) {
// do something...
};

// 如果 number 嚴格相等為 數值 的結果相反為 true 就執行
// 這邊直接理解為回傳的布林直接轉為相反會比較好記
if(number !== Number) {
// do something...
};

函式小補充

關於上面提到的函式其實還有一種用法是 閉包工廠模式 。我們說過在 JavaScript 創建階段時,會進行變數的宣告,而這時候就會在記憶體內佔了一個空間,那我們可以思考,如果在函式內創建變數呢?是否會讓記憶體更省呢,因為函式內的變數完成動作後就不會在記憶體上直到再次呼叫這個函式,所以就出現了 閉包 這種方法。以下有個簡單的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
function deposit() {
var base = 100; // 最低儲值金額
return function (money) {
base = base + money; // 加上儲值的金額
return base; // 返回總共儲值的金額
}
} -> 閉包的 function

// 執行 function
var RexDeposit = deposit(); // 返回 100
var RexDeposit = deposit(); // 因記憶體不會釋放 所以會返回 200
var BuzzDeposit = deposit(200); // 返回 300
var BuzzDeposit = deposit(200); // 返回 500

閉包 延伸而出我們能探討到 工廠模式,既然你已經明白 function 就是丟東西進去,然後他幫你產出一樣你預期內的產品,那何不讓工廠內進行分組呢?一個幫你 生產,一個幫你 組裝,一個幫你 售出,所以後來又出現了 私有方法 即是在工廠模式內 放入好幾個 function 然後各自可被呼叫,你會得到你預期的東西。下方有個簡單的例子:

1
2
3
4
5
6
7
8
9
10
11
12
function deposit(baseMoney) {
var base = baseMoney || 100; // 最低儲值金額
return {
addMoney: function() { <-- 生產
// do something...
},
spendMoney: function() { <-- 組裝
// do something...
},
xxxxxx: function... // 更多 function <-- 售出
}
}

詳細閉包可參考 傳送門


Conclusion & 結論

雖然本章的內容較少,但卻是在使用 JavaScript 時息息相關的,有時候最基礎的東西,卻會影響日後寫程式時的邏輯順暢程度, 雖然菜雞我目前還在往偉大的航道上努力著,但我相信努力只會使自己越來越好。

這次筆記到目前是第二篇,想想從接觸六角學院的地下城開始寫部落格後,寫部落格是繼教別人第二快且扎實的學習方式,不明白就爬文,不清楚就實作,這就是自己寫部落格的初衷,我想一直告訴自己,永遠別忘了當初為什麼會開始敲鍵盤,也別迷失了自己,致自己。


參考網站