資料型別
- 可以用
typeof
來判斷型態
其中除 object 以外六種原始類型合稱為==Primitive type==,其特性是==不能改變 Immutable==
typeof null === 'object'
==其中 null 會被判斷是 object==,是 javascript 廣為人知的 bug- 因為 array 會被判定為 object,可以換用
Array.isArray()
來判斷更準確。 ==當字串跟數字「比大小」的時候,會被轉成數字來比較==
字串 + 字串 可以直接合併
let newStr = ''; newStr += 'a'; console.log(newStr) //a
當字串 + 數字的時候,不會數字相加,而是直接合併
例如:1 + '3' = 13
可以用
typeof
檢查目標有沒有被賦值if (typeof a !== 'undefined') { console.log(a) }
物件
例如當我把資料存取成陣列,但陣列的一個元素裡面又有 1 個以上的以元素時,可以改用物件來存取。
- EX: LIOJ1033
輸入的每一行資料都代表平面座標的 (x, y) 點
input:
2 2
1 1
10 10
100 100let dots = [] for (let i = 1; i < lines.length; i++) { let temp = lines[i].split(' ') dots.push ({ x: Number(temp[0]), y: Number(temp[1]) }) // dot => [ // { x: 2, y: 2 }, // { x: 1, y: 1 }, // { x: 10, y: 10 }, // { x: 100, y: 100 } // ] }
讀取物件內容
操作物件時,你需要透過它的 key 來存取物件的屬性。這在寫法上有兩種方式:
Dot notation
Bracket notation
兩種方式的範例分別如下:// dot notation flashcardEntry.word // bracket notation flashcardEntry['word']
- 如果使用 dot notation,表示你在呼叫物件的屬性,或者可以說,你在呼叫
flashcardEntry
物件的word
方法 ;如果使用 bracket notation,意義比較像你使用word
這個 key 想取出flashcardEntry
的內容。
比較運算
== & === 判斷:JS Comparison Table
- 設置條件時,只用
===
,不要用==
,例如
if (score === 100){
console.log('good')
}
console.log(2 == '2') // true,因為會轉換型態
console.log(2 === '2')
//false,不會轉換型態,所以型態不一樣就會 false
- ==在等號比較 object(包含 array, function 等等)時,是依據他們有沒有指向同一記憶體位置來判斷==
console.log({} === {}) // false
- ==特殊規則:
NaN
(型態為 number)不等於任何東西,包含自己==
console.log(NaN === NaN) // false
let a = Number('hello')
console.log (a) // NaN,因為沒東西可轉成數字
console.log (typeof a) // number,是數字的一種但不是數字
console.log(a === a) // false
比較運算子有時候會遇到不同型別要「自動轉型」的狀況,規則大致如下:
- 兩者都是數字,則單純就其字面大小比較。
- 如果其中一個是數字,而另一個不是,則會嘗試在背後將另一個先轉為數字後再比較。
- 如果兩者都是字串,則會依照字母的順序 (standard lexicographical ordering) 來進行比較。[註1] 實戰題:LIOJ1004
- 如果其中一者是 boolean 的情況,則會把
true
看成1
,false
看成0
再進行比較。 - 如果是物件的情況下,則是會先透過物件的
valueOf()
方法先求得對應的數值,若物件沒有valueOf()
方法的話,則會透過toString()
轉型再進行比較。 - [註1]:standard lexicographical ordering:簡單來說,字串的順序可以想像成「英語字典」中的單字,第一個字母、以 a、b、c ... 到 z 的順序排列;如果第一個字母一樣,那麼比較第二個、第三個乃至後面的字母。需要注意的是,大小寫字母 unicode 裡的順序是不一樣的。
- 參考資料
Primitive type 與 object 之差異
- ==Primitive type Immutable(不能改變)==
let str = 'hello';
let newStr = str.toUpperCase();
console.log(str);
//還是 hello 不會改變,而 newStr 就是新的字串 HELLO
// 但 object 不一樣
let arr = [1];
arr.push(2);
console.log(arr);
// [1, 2] 此時陣列就被改變了
- 當宣告一個變數是一個 object 物件時,會將物件放置於某個記憶體位置,因此當兩個值相等的物件或陣列時,經 js 判斷後並不相等,因為他們指向的記憶體位置並不相同。簡而言之,==object 型態設定變數會先指向某個記憶體,再從記憶體位置延伸去設值,而 primitive type 則是直接設值==。
下圖的值皆為 false
- Ex1:
==primitive type 直接設值 (call by value),各自有各自的記憶體位置==
a: 10
b: 10 -> b: 20
var a = 10 // a: 10
var b = a // b: 10
b = 20 // b: 20
console.log(a, b) // 10, 20
==object 指向記憶體位置(call by refernece)==
0x01: {
a: 1
}
obj: 0x01 // obj 宣告為變數時的放置物件的記憶體位置
obj2: 0x01 // 當 var obj2 = obj 時 obj2 放置的記憶體位置
var obj = {
a: 1
}
var obj2 = obj
// 這時 obj2 會被賦予和 obj 相同的記憶體位置
obj2.a = 2
// 此時 obj2 所指向的 0x01 裡面的 a 就被改變,
// 因為前面把 obj2 放置物件的記憶體位置改成 0x01
0x01: {
a: 2
}
console.log(obj === obj2) // true
// 用 === 比較兩個 obj 時,是判斷記憶體位置
==此段回傳值是 true,因為用 ===
比較兩個 obj 時,是以記憶體位置來判斷而不是比較他們值==,而此時 obj, obj2 指向同一個記憶體位置,因此改變 obj2 的 a 值會連 obj 的 a 也一起改成 2,兩者判斷還是一樣
小練習:
var a = [1]
var b = a
b.push(2)
console.log(a.length) // 2
// 因為在 var b = a 時,a & b 都指向同一記憶體位置
// 所以改 b 也會改到 a,都是 [1, 2]
參考資料:Javascript 基礎打底系列 (一) - 常見的出包狀況解析
小練習:JS - call by value / reference 練習題
==object.something = 10(更改屬性內容) 和 object 重新賦值的差異==(array 亦同):
==object.something = 10 >>> by reference==
var obj = {
a: 1
}
0x01: {
a: 1
}
obj = 0x01
var obj2 = obj
obj2 = 0x01
console.log(obj, obj2) // {a: 1}, {a: 1}
obj2.a = 2
// 表示我要改 obj2 的記憶體位置的物件內的元素 a 的值
console.log(obj, obj2) //{a: 2}, {a: 2}
// 因為兩個 obj 指向同一記憶體位置,所以兩者都被改變
==obj 重新賦值 >>> by value,也有叫做 by sharing==
var obj = {
a: 1
}
// 當 obj 的變數設立時
0x10: {
a: 1
}
obj = 0x10
var obj2 = obj
// 這時 obj2 會被賦予和 obj 相同的記憶體位置
obj2 = 0x10
console.log(obj, obj2) // {a: 1}, {a: 1}
obj2 = {
b: 1
}
// 當 obj2 的設立一個新物件時,該物件會重新指向一個新的記憶體位置
0x20: {
b: 1
}
// 然後將新的記憶體位置賦予 obj2
obj2 = 0x20 // 此時 obj2 指向的記憶體位置從 0x10 變為 0x20
console.log(obj === obj2) // false
console.log(obj , obj2) // {a: 1}, {b: 1}
// 因為此時兩個 obj 已指向不同記憶體位置,所以只會改到 obj2
此段回傳值是 false,因為 obj2 已經被賦予一個新的物件,指向的記憶體位置就會改變,就跟 obj 不一樣了。
==兩個物件之間等號比較是以他們的記憶體位置來判斷==,而 primitive type 則是單純比較他們的值
延伸:所以在 const 宣告的情況下,primitive type 的值被改變會出錯,物件的記憶體位置被改變會出錯(單純改值就不會,因為記憶體位置沒變)
詳見:變數的作用域 Scope 與 var, let, const 差異
let arr = [1]
let arr2 = [1]
console.log(arr === arr2) // false
// 因為兩個陣列指向不同記憶體位置。
// 因為在設立變數時先把 obj 放到某個記憶體位置
0x01: [1]
0x02: [1]
// 再將變數設置到該記憶體位置
arr: 0x01
arr2: 0x02
----
var obj = {a: 1}
var obj2 = obj
obj2.a = 2
console.log(obj === obj2) // true
// 因為兩者指向同一記憶體位置
---
var obj = {a: 1}
var obj2 = obj
obj2 = {a: 2}
console.log(obj === obj2) // false
// 因為兩者指向不同記憶體位置
---
console.log(2 == "2") // true 因為轉換成相同型態
console.log(2 === "2") // false
延伸閱讀:
談談 JavaScript 中 by reference 和 by value 的重要觀念
深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?
邏輯運算
- Ex:
30 || 6 >> 30 6 || 30 >> 6 6 && 30 >> 30 30 && 6 >> 6 0 || 30 >> 30 30 || 0 >> 30 0 && 30 >> 0 30 && 0 >> 0 0 || 0 >> 0 0 && 0 >> 0