Yakim shu Hi, 這是我擴充腦內海馬體的地方。

[第二週] 基礎 JavaScript - 02 變數

變數的命名方式

變數的命名有一套規則是非常重要的,語意化的命名可以一看名稱、就知道它的用處是什麼,才不用看整個程式架構來猜,非常的浪費時間。

常見的命名方式有駝峰式、匈牙利命名法

參考資料:


++int VS int++

這還蠻酷的,原來把 ++ 放前面或後面,執行的順序是不一樣的!

var int1 = 0;
var int2 = 0;
console.log(int1++ && 30, "int1: " + int1); // 輸出 0, int1: 1
console.log(++int2 && 30, "int2: " + int2); // 輸出 30, int2: 1

兩個變數的輸出結果都是 1,這沒有問題。

但為什麼前面的邏輯比較不一樣勒?因為 int1++ 是會先跑完整句、才執行。所以當 int1++ && 30 的時候、變數 int1 的數值其實還是 0,是等到比較完了 int1+1


變數類型

在 JavaScript 中,除了原始類型,其他一切都物件。可以使用 typeof <Variable> 查看變數類型。

此外因為 JavaScript 非常的隨性,就算一開始定義好了 變數1 的類型,後來還是可以再做更改,當然沒事請不要這麼做就是了。

以下範例就是教你:「 如何造成別人跟自己的困擾 」:

var intNum = 3;
intNum = "hahaha";
console.log(typeof intNum); // 會輸出 string,但此變數名稱會變得非常擾民

參考資料:


陣列

通常是放同類型的資料,例如 arrNum = [10, 20, 5, 7]arrString = ["小明", "小花", "小黑"]

複習一下以前寫的陣列筆記:【筆記】Javascript大全 - 05(一) 陣列

物件

key: value 組成,取 value 的方法有兩種:

看以下 3 種方法都會輸出 小黑,而也可以將 objectarrayfunction 放入 value 裡面:

var changeKey = "dog";
var oProfile = {
    name: "Yakim",
    dog: "小黑", 
    oFather : {
        name: "LingPiao",
        dog: "小花"
    }
}

console.log(oProfile.dog);  // 小黑
console.log(oProfile["dog"]); // 小黑
console.log(oProfile[changeKey]); // 小黑
console.log(oProfile.oFather.dog); // 小花 ← 放入物件

參考資料:


變數運算要注意陷阱!

陷阱一:注意型別

運算子 +- 要特別注意型別。

例如:要是 <string> + <num>, 會先將 num 改成 string,再做字串相加: <string> + <string>,例如:

'1' + 2 // 輸出 12 ( 變成 '1' + '2')

參考以前筆記:【筆記】Javascript大全 - 02 運算式與運算子

將 string 轉成 number 的方式有兩種:

  1. Number(<string>)
  2. parseInt(<string>, 10) : 第 2 個參數為要轉換的進位制,例如 2, 10, 16

陷阱二:注意浮點數 float

console.log(0.1+0.2); // 0.30000000000000004 

這是一個很重要的觀念,事實上不只 JavaScript,所有的程式語言都會有這個問題。

因為輸入的值本身就不精確,且電腦記憶體有限,運算結果也很有可能造成誤差,這點在 CS50 也有提到,就不再寫了:【筆記】CS50 - week 1 ( 2019年更新 ) ( 文章最後面 )

但說實在,要搞懂為什麼還蠻困難的,根據 JavaScript 浮点数陷阱及解法 指出:

( 此篇文章我只看得懂這段… )

首先要搞清楚 JavaScript 如何存储小数。和其它语言如 Java 和 Python 不同,JavaScript 中所有数字包括整数和小数都只有一种类型 — Number。它的实现遵循 IEEE 754 标准,使用 64 位固定长度来表示,也就是标准的 double 双精度浮点数(相关的还有float 32位单精度)。

// 0.1 和 0.2 都转化成二进制后再进行运算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

// 转成十进制正好是 0.30000000000000004

解決方法

網路上有非常多文章再討論如何解決浮點數精度問題,看得我頭昏眼花,整合了一下,假設要取浮點數 result

  1. 不要用…
  2. 引入數學庫 library
  3. parseFloat(result).toFixed(x)x 填入 0~20位數,一般不建議使用
  4. Math.round(result * 100000) / 100000;: 先把數字變成更大的數值在去掉不想要的尾數變成整數後,再縮回原本位數
  5. 使用加減乘除函式(有點複雜):將浮點數轉為字串,把字串分割成整數位 & 小數位,進行完運算後,再將結果轉回浮點數,有興趣可以看:JavaScript 浮点数运算的精度问题

參考資料:


== VS ===

在之前的筆記裡 :【筆記】Javascript大全 - 02 運算式與運算子 也有提到在 == 比較式中,JavaScript 會先幫兩邊不同的變數類型、先進行類型轉換、再進行比較。

所以 "1" == true // 回傳 true 當中,在比較之前,會先執行以下兩步驟:

結論:為了避免對變數類型的疑慮,建議平常都使用嚴格相等 ===


JavaScript 中的物件特性

非常重要的觀念,一定要釐清。

記憶體位置

假設有兩個物件 objobj2

var obj1 = {a: 1};
var obj2 = {a: 1};
console.log(obj1 === obj2); // false

為什麼上面的兩個看似內容一樣的 object 卻回傳 false? 因為在 JavaScript 中,物件是比較記憶體位置

物件的更新

一樣是兩個物件 objobj2,但此例中的 obj2 是指向 obj

var obj = {a: 1};
var obj2 = obj;

更改 obj2.a 的值時,觀察以下 obj 的變化。

obj2.a = 2;
console.log(obj); // 2
console.log(obj === obj2); // true,記憶體位置相同

因為 obj2obj 都指向同一個位置,而那裡存放的內容是 {a: 1}。 所以當 obj2.a 改成 2,因為指向的位置是一樣的,所以 obj.a 也會一起更改

螢幕快照 2019-04-23 下午8.35.29 ( 上圖為課程截圖 )


第二個例子就比較奇特了:

obj2 = {b: 1};
console.log(obj); // {a: 2}
console.log(obj2); // {b: 1}
console.log(obj === obj2); // false,記憶體位置不同,連結被斷開了(登愣!)

物件本身 obj2 被傳入新的內容,此時 objobj2 的連結就被斷開了。

螢幕快照 2019-04-23 下午8.35.51 ( 上圖為課程截圖 )


三元運算子

當只有一個判斷式 Condition 的時候,有兩個實用小技巧。

舉一個成績判斷為例子。

var score = 70;
var result;
if (score >= 60) {
    result = true;
} else {
    result = false;
}

技巧一:

如果只是輸出 boolean 值 ( true or false ) 的話,其實可以改寫成:

var score = 70;
var result = score >= 60;

那如果輸出值不是 boolean、而是 string,如以下範例:

var score = 70;
if (score >= 60) {
    console.log("pass");
} else {
    console.log("fail");
}

####技巧二: 也可以利用三元運算式來簡寫成: Condition ? A : B

所以將剛剛的範例改成 :

var score = 70;
var result = score >= 60 ? "pass" : "fail"

( 以上內容大部分是 程式導師實驗計畫第三期 的學習筆記,如有錯誤歡迎糾正,非常感謝 🤓 )