前端基礎學習之關於 JavaScript 的數據類型

7中數據類型

  • 空(Null)
  • 未定義(Undefined)
  • 數字(Number)
  • 字符串(String)
  • 布爾值(Boolean)
  • 符號(Symbol)
  • 對象(Object)

Undefined

只有一個值“undefined”。得到undefined的幾種方式:

  • 引用已聲明但未初始化的變量;
  • 引用未定義的對象屬性;
  • 執行無返回值函數;
  • 執行 void 表達式;
  • 全局常量 window.undefined 或 undefined。

“void 0” 表達式常用來代表不執行任何操作,實例如下:

x>0 && x<5 ? fn() : void 0;

判斷變量值是否爲undefined常用的方式:

if(typeof x === 'undefined') {
  ...
}

注意,直接使用if(!x)和if(x===undefined)是不可取的,因爲只要變量 x 的值爲 undefined、空字符串、數值 0、null 時都會判斷爲真。直接使用三等號===,當x爲undefined,那麼是會拋出錯誤 “ReferenceError: x is not defined” 導致程序執行終止。

Null

表示空值,只有唯一值null。

Boolean

Boolean 數據類型只有兩個值:true 和 false,分別代表真和假。
需要注意,我們在if語句中進行條件判斷時,if語句實惠進行類型轉換的,將判斷條件的變量轉換成 Boolean 數據類型,而 0、空字符串、null、undefined 在轉換時都會返回 false。如下代碼,傳入0時,返回的是undefined,而不是我們期望的"星期日"。

function getWeek(week) {
  const dict = ['日', '一', '二', '三', '四', '五', '六'];
  if(week) return `星期${dict[week]}`;
}

Number

兩個重要值

Number 是數值類型,有 2 個特殊數值得注意一下,即 NaN 和 Infinity。

  • NaN(Not a Number)通常在計算失敗的時候會得到該值。要判斷一個變量是否爲 NaN,則可以通過 Number.isNaN 函數進行判斷。
  • Infinity 是無窮大,加上負號 “-” 會變成無窮小,在某些場景下比較有用,比如通過數值來表示權重或者優先級,Infinity 可以表示最高優先級或最大權重。

進制轉換

將其他進制的整數轉換成十進制顯示的時候可以使用 parseInt 函數,此函數有來兩個參數,第一個爲數字或字符串,第二個爲進制數,默認爲10,當進制數轉換失敗時會返回 NaN。

['0', '1', '2'].map(parseInt) // [0, NaN, NaN]

將十進制轉換成其他進制時,可以通過 toString 函數來實現。

(10).toString(2) // "1010"

精度問題

在JavaScript中,我們進行浮點數運算時,往往會遇到精度問題。如下:

0.1 + 0.2 // 0.30000000000000004//並不是期望的0.3
Math.pow(Math.pow(5, 1/2), 2) // 5.000000000000001

原因

JavaScript進行計算時,JavaScript 引擎會先將十進制數轉換爲二進制,然後進行加法運算,再將所得結果轉換爲十進制。

解決方法

兩種方式消除無限小數位。

  • 先轉換成整數進行計算,然後再轉換回小數,這種方式適合在小數位不是很多的時候。
  • 捨棄末尾的小數位。比如對上面的加法就可以先調用 toPrecision 截取 12 位,然後調用 parseFloat 函數轉換回浮點數。parseFloat((0.1 + 0.2).toPrecision(12)) // 0.3

String

最常用的類型,相關的方法比較多,不一一介紹,可查看官方文檔

千位分隔符的兩種實現方式:

//從右往左遍歷數值每一位,每隔 3 位添加分隔符。將字符串數據轉化成引用類型數據,即用數組來實現。
function sep(n) {
  let [i, c] = n.toString().split(/(\.\d+)/)
  return i.split('').reverse().map((c, idx) => (idx+1) % 3 === 0 ? ',' + c: c).reverse().join('').replace(/^,/, '') + c
}
//通過引用類型,即用正則表達式對字符進行替換來實現
function sep2(n){
  let str = n.toString()
  str.indexOf('.') < 0 ? str+= '.' : void 0
  return str.replace(/(\d)(?=(\d{3})+\.)/g, '$1,').replace(/\.$/, '')
}

Symbol

定義

Symbol 是 ES6 中引入的新數據類型,它表示一個唯一的常量,通過 Symbol 函數來創建對應的數據類型,創建時可以添加變量描述,該變量描述在傳入時會被強行轉換成字符串進行存儲。

一句話概括 Symbol 生成一個全局唯一的值。

常用於兩個場景 避免常量值重複避免對象屬性覆蓋

補充知識:類型轉換

類型轉換定義

在處理不同數據類型運算或邏輯操作時會強制轉換成同一數據類型。

通常強制轉換的目標數據類型爲 String、Number、Boolean 這三種。如下爲轉換關係:
轉換關係

常見的觸發類型轉換的操作

  • 運算相關的操作符包括 +、-、+=、++、* 、/、%、<<、& 等。
  • 數據比較相關的操作符包括 >、<、== 、<=、>=、三等號===
  • 邏輯判斷相關的操作符包括 &&、!、||、三目運算符。

Object

引用類型,即鍵值對的集合。

淺拷貝和深拷貝:

  • 由於引用類型在賦值時只傳遞指針,這種拷貝方式稱爲淺拷貝。
  • 而創建一個新的與之相同的引用類型數據的過程稱之爲深拷貝。

通過typeof查看數據類型的描述:

[undefined, null, true, '', 0, Symbol(), {}].map(it => typeof it)// ["undefined", "object", "boolean", "string", "number", "symbol", "object"]

發現 null 有些特殊,返回結果和 Object 類型一樣都爲"object",所以需要再次進行判斷。按照上面分析的結論,我們可以寫出下面的函數:

function clone(data) {
  let result = {}
  const keys = [...Object.getOwnPropertyNames(data), ...Object.getOwnPropertySymbols(data)]
  if(!keys.length) return data
  keys.forEach(key => {
    let item = data[key]
    if (typeof item === 'object' && item) {//避免null
      result[key] = clone(item)
    } else {
      result[key] = item
    }
  })
  return result
}

在遍歷 Object 類型數據時,我們需要把 Symbol 數據類型也考慮進來,所以不能通過 Object.keys 獲取鍵名或 for…in 方式遍歷,而是通過 getOwnPropertyNames 和 getOwnPropertySymbols 函數將鍵名組合成數組,然後進行遍歷。對於鍵數組長度爲 0 的非 Object 類型的數據可直接返回,然後再遍歷遞歸,最終實現拷貝。

由於嵌套調用,因此當對象數據嵌套時,會出現死循環。怎麼避免這種情況呢?一種簡單的方式就是把已添加的對象記錄下來,這樣下次碰到相同的對象引用時,直接指向記錄中的對象即可。要實現這個記錄功能,我們可以藉助 ES6 推出的 WeakMap 對象,該對象是一組鍵/值對的集合,其中的鍵是弱引用的。其鍵必須是對象,而值可以是任意的。

function clone(obj) {
  let map = new WeakMap()//記錄已經拷貝過的對象
  function deep(data) {
    let result = {}
    const keys = [...Object.getOwnPropertyNames(data), ...Object.getOwnPropertySymbols(data)]
    if(!keys.length) return data 
    const exist = map.get(data)
    if (exist) return exist //重新創建一個對象並加入 WeakMap 中
    map.set(data, result)//重新創建一個對象並加入 WeakMap 中
    keys.forEach(key => {
      let item = data[key]
      if (typeof item === 'object' && item) {
        result[key] = deep(item)
      } else {
        result[key] = item
      }
    })
    return result
  }
  return deep(obj)
}

總結

  • 7種類型:空(Null)、未定義(Undefined)、數字(Number)、字符串(String)、布爾值(Boolean)、符號(Symbol)、對象(Object)。
  • Number精度問題。
  • 進制轉換parseInt和toString函數。
  • Symbol 生成一個全局唯一的值。
  • Object深拷貝,getOwnPropertyNames 和 getOwnPropertySymbols 函數。
  • WeakMap解決深拷貝遞歸調用死循環問題。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章