讀書筆記: JavaScript技術手冊2 - 基本語法(型別、變數、運算)

REPL

  • node -e "console.log('Hello World')";

     > node -e "1 + 1"
    
     > node -p "1 + 1"
     2
    
  • Read-Eval-Print-Loop
    • .editor
    • .help
    • console.log(_);
        // CMD
        > node
        > 'Hello World'
        Hello World
        > console.log(_)
        Hello World
        undefined
      

Data Type 資料型態與變數

  • JavaScript 屬於動態定型語言
    • 靜態定型(statically-typed):型態資訊記錄在變數
    • 動態定型(dynamically-typed):型態資訊要從執行時期的物件取得
  • Duck typing 鴨子定型
  • 變數宣告:letconstvar
    • Primitive type:變數儲存被指定的值
    • Composite type:變數永遠只是個參考至物件的名稱
      • 所以以下情況成立
          // const 只是不能重新指定陣列變數名稱,陣列本身是mutable
          const arr=[1, 2, 3];
          arr[1] = 10;          // [10, 2, 3];
        
  • Hoisting
    • var 變數在未被宣告前使用會被賦值 undefined
    • let 也會有 Hoisting 行為,但不會被賦值 undefined,所以變數在完全沒有值的情況下存取就會出錯。
  • strict mode (嚴格模式)
    • .js 檔案開頭寫 use strict
    • 避免一些模糊不清的奇怪部分跑出來
    • 例如
      1. 沒有用關鍵字宣告的變數,不管在哪裡被賦值都會變成全域變數
      2. 保留字不能當變數名稱

Primitive

  • number
  • string
  • boolean
  • undefined
  • null
  • Symbol

number

  • \ 64bit float only
  • 執行時期都是以浮點數表示
  • 不同進位的 10 表示法
      10
      0xA
      0o12
      0b1010
    
  • 字串剖析為數字
    • Number('10');
    • parseInt(), parseFloat() (※無法處理 0b, 0o, 0x)
  • 最大值、最小值與安全範圍

      Number.MIN_VALUE
      Number.MAX_VALUE
      Number.MIN_SAFE_INTEGER
      Number.MAX_SAFE_INTEGER
    
      Number.isInteger()
      Number.isSafeInteger(9007199254740991)   // true
      Number.isSafeInteger(9007199254740992)   // false
    
  • 運算
      1 / 0                                    // Infinity
      1 / 0 === Infinity                       // true
      Infinity === Number.POSITIVE_INFINITY    // true
      -Infinity === Number.NEGATIVE_INFINITY   // true
    
  • NaN
    • 開發避免NaN的解決方法之一就是避免JS的自動型別轉換
      0 / 0                // NaN
      NaN === NaN          // false
      NaN === Number.NaN   // false
      

string

  • Immutable
  • empty string: '', \0, \x00
  • 字串字元數計算問題
    • 碼點(code point): \uD834\uDD1Eu{1D11E}
    • 碼元(code unit): \uD834
    • 字串元素單位:UTF-16碼元
    • 要注意字元碼點是否超出[U+0000, U+FFFF],若字元碼點
      • 在[U+0000, U+FFFF]內:.length ===字元數量(因為UCS-2系統是2bytes為一個碼元)
      • 超出[U+0000, U+FFFF]:.length ===碼元數量
      • \u{1D22E}: 𝄞 (1字元)
      • \u{1D22E}.length === 2
    • 正確取得某個字元的方法
        // '\uD834\uDD1E'==='u{1D11E}'
        '\u{1D11E}'.charAt(0)        // '\uD834'
        '\u{1D11E}'.codePointAt(0)   // 119070
        String.fromCodePoint(119070) // 'u{1D11E}'
      
    • 字元超出[U+0000, U+FFFF]取得字元數的方法
       Array.from('\u{1D11E}').length   // 1
      

undefined

  • typeof undefined // 'undefined'
  • 不是保留字,可以用 void 避免這種狀況
      let undefined = 10;
      console.log(undefined);   // ok
    

null

  • 表示沒有物件
    • 例如參考至某物件的變數沒有參考對象,就可以賦值 null 達成這個目的。
  • typeof null // object

Composite 複合型態

Object

  • Object individuation: delete
  • in operator 可檢查物件是否有此屬性名稱
  • 存取物件沒有的屬性 → return undefined;
  • 但要避免將物件的屬性值設定為 undefined

      let obj = {x: undefined};
    
      console.log(x);            // undefined
      console.log('x' in obj);   // true
    

     - this 關鍵字

    1. 最基本用法:參考至 . operator 左邊的物件

       function forEach(callback){
           for(let i = 0; i < this.length; ++i){
               callback(this[i]);
           }
       }
      
       let arrayLikeObj = {
           '0': 100,
           '1': 200,
           '2': 300,
           length: 3,
           forEach: forEach
       }
       obj.forEach(function(elem){
           console.log(elem);
       });
      
    2. this 參考對象其實會以呼叫方式而定,並非以是否附屬於某個物件而定。

Array

  • typeof []; // object
  • JavaScript的陣列產生方式不是預先在記憶體佔據連續的線性空間
    • 比較像是以數字作為key的物件
        let array = [10, 20, 30];
        console.log(array['1']);   // 10
      
  • 類陣列物件:所以其實可以用物件模擬陣列(※這是用Object instance創建的陣列,並非真正的Array)
      // array-like consecutive objects
      let arrayLikeObject = {
          '0': 10,
          '1': 20,
          '2': 30,
          length: 3
      };
    
    • Array.from(); 可以把類陣列物件(e.g., string)轉換成真正的Array
        Array.from(arrayLikeObject);   // [10, 20, 30]
        Array.from("lun");           // ['l', 'u', 'n']
      
  • 改變 Array.length 的值不會擴充 Array,只有改變 length 的值同時產生 empty items (空項目)
  • 要避免陣列產生 empty items (容易有 bug),因為多出來的 empty items 不會有索引:

      let arr1 = [undefined, undefined];
      let arr2 = [];
      arr2.length = 2;
    
      console.log('0' in arr1);               // true
      console.log('0' in arr2);               // false
    
    • forEach()filter() 也會跳過 empty items 且不會顯示結果;map() 雖然也會跳過 empty items 卻會顯示在結果:
        [,,].forEach(elem => console.log(elem));  //
        [,,].filter(elem=>true);                  //
        [,,].map(elem=>1);                        // [<2 empty items>]
      

運算

  • +、-、*、/、** 不要用在number和string以外的型態
      [] + [];   // ''
      [] + {};   // '[object Object]'
      {} + [];   // 0
      {} + {};   // NaN
    
  • ===!==
    • ===
      • Primitive type: 比較兩個變數儲存的值和型態
      • Composite type: 比較兩個變數是否參考至同一物件
    • 不要再用 ==!=
        '' == 0;             // true
        null == undefined;   // true
        1 == true;           // true
      
    • ECMAScript 6 相等性
  • Falsy value
    • false
    • ''
    • 0、'0'
    • NaN
    • null
    • undefined
  • 捷徑運算

      'left' && 'right'   // 'right'
      0 && 'right'        // 0
      'left' && 0         // 0
    
      // 常用範例: 預設值
      let name = '';
      alert(name || 'Guest');   // 'Guest' 為預設值
    
  • (逐)位元運算

      // 2's component
      0b10010001 & 0b01000001   // 00000001 -> 十進位: 1
    
      0b0011    // 3
      ~0b0011   // -4
      0b1111    // 15
      ~0b1111   // -16
    
  • others
    • 指定運算子
    • 遞增/遞減運算子
    • in 確認物件是否包含某個屬性(不考慮可否列舉且也會列出繼承的屬性)
        // length, toString are non-enumerable
        'length' in [];    // true
        'toString' in {}   // true
      
      • hasOwnProperty() 只會列出物件本身擁有的屬性
          [].hasOwnProperty('length');     // true
          {}.hasOwnProperty('toString');   // false
        
    • instanceof

References