資料型別與運算符號


Posted by ericcch24 on 2020-10-16

資料型別

  • 可以用 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 100

    let 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 看成 1false 看成 0 再進行比較。
  • 如果是物件的情況下,則是會先透過物件的 valueOf()方法先求得對應的數值,若物件沒有 valueOf() 方法的話,則會透過 toString() 轉型再進行比較。
  • [註1]:standard lexicographical ordering:簡單來說,字串的順序可以想像成「英語字典」中的單字,第一個字母、以 a、b、c ... 到 z 的順序排列;如果第一個字母一樣,那麼比較第二個、第三個乃至後面的字母。需要注意的是,大小寫字母 unicode 裡的順序是不一樣的。
  • 參考資料

Primitive type 與 object 之差異

  1. ==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] 此時陣列就被改變了
  1. 當宣告一個變數是一個 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
    

特殊運算


賦值運算

tags: Week16 Week2

#week16







Related Posts

Laravel and Cypress integration

Laravel and Cypress integration

Explore-Binary Tree

Explore-Binary Tree

JavaScript 程式執行原理:JS中的物件導向

JavaScript 程式執行原理:JS中的物件導向


Comments