介紹
犀牛書第三章主要講了型別、值還有變數,本篇主要講 資料型別。
需要注意引用型別和非引用型別的區別、-0、NaN、八進位制、字串編碼等問題。
型別轉換部分檢視:
自我提問
- 資料型別怎麼區分?
- 什麼時候會觸發隱式型別轉換?
- 浮點計算為何會丟失精度?
0.1 + 0.2 === 0.3; 複製程式碼
- 字串中有什麼坑?
var e = '?'; console.log(e.length); 複製程式碼
- 0 和 -0 是否完全一樣?
0 === -0; 1/0 === 1/-0; 複製程式碼
腦圖
關鍵知識點
資料型別
資料型別主要分為兩類:
- 原始型別(也稱為 值型別、基本型別、不可變型別、非引用型別),原始型別包括 數字、字串、布林值、null、undefined(以及 es6 中的 Symbol )。
- 物件型別(也稱為 可變型別、引用型別),非原始型別的所有型別均為物件型別(普通物件、陣列、函式、正則、日期等等)。
隱式型別轉化
JavaScript 會在 程式期待使用對應資料型別 的地方 自動進行型別轉換(常說的 隱式型別轉換,如將非 Boolean 型的資料放在 if 判斷條件中、使用非字串和數字進行 + 運算等)。
弱型別
JavaScript 變數是 無型別的,變數 可以賦予任何型別的值,並且 可以重新賦予不同型別的值(也就是第一章所說的 弱型別 概念)。
詞法作用域
JavaScript 採用 詞法作用域(也稱 靜態作用域,正是因為詞法作用域的緣故,才會出現閉包,在變數定義的時候,它的作用域就已經確定好了)。
IEEE 754 標準
JavaScript 中的數字採用 IEEE 754 標準,整數範圍為 -(2**53) ~ 2**53
(Number.MAX_SAFE_INTEGER
的值為 2**53 - 1
,和這裡相差了 1,因為 超出(安全)範圍的值無法保證精度,故而最大值本身是不安全的,可能由某個失去精度的值轉換而來️)。
下面是 MDN 中的例子。
var x = Number.MAX_SAFE_INTEGER + 1;
var y = Number.MAX_SAFE_INTEGER + 2;
// 9007199254740991 9007199254740992 9007199254740992
console.log(Number.MAX_SAFE_INTEGER, x, y);
// true
console.log(x === y);
複製程式碼
實際操作基於 32 位整數
JavaScript 中的實際操作如陣列索引等 是基於 32 位整數 的。
// 4294967296
console.log(2**32);
// 安全
new Array(2**32 - 1);
// 報錯:RangeError: Invalid array length
new Array(2**32);
複製程式碼
特殊的 NaN
NaN 的意思是 Not a Number,不過 typeof NaN 是 number,所以它是一個不是(有效)數字的數字值。
由於 NaN 不等於任何值包括自身,可以使用 a !== a 來判斷 a 是不是一個 NaN 值。
八進位制
八進位制雖然大部分瀏覽器支援,但是它本身是 非標準的,在嚴格模式下 明令禁止(八進位制使用 0 作為前置標識,這會在某些地方引起歧義)。
// 9
console.log(09);
// 15
console.log(017);
// 上述都是前導 0 但是卻按照兩種進位制去進行了解析
(function() {
'use strict';
// 嚴格模式下會報錯:SyntaxError: Octal literals are not allowed in strict mode.
console.log(09);
console.log(017);
})();
複製程式碼
浮點數計算精度
JavaScript 中的浮點數計算精度問題是由於採用了 IEEE 754 浮點數表示法(大部分程式語言都是採用這個標準,所以這個問題真不是 JavaScript 的設計缺陷之類的),二進位制表示法 導致十進位制的小數無法精確的標識,所以導致一系列浮點計算的精度問題。
var a = 0.3 - 0.2;
var b = 0.2 - 0.1;
// false
console.log(a === b);
// 0.09999999999999998 0.1
console.log(a, b);
複製程式碼
這種時候建議使用整數來進行計算:
var a = (0.3 * 10 - 0.2 * 10) / 10;
var b = (0.2 * 10 - 0.1 * 10) / 10;
// true
console.log(a === b);
// 0.1 0.1
console.log(a, b);
複製程式碼
特殊的 -0
0 和 -0 並不完全對等。
var a = 0, b = -0;
// 全等號會認為他們相等
console.log(a === b);
// 但是進行除法計算時將會影響計算結果
// Infinity -Infinity false
console.log(1/a, 1/b, 1/a === 1/b);
複製程式碼
字串編碼導致的奇怪問題
JavaScript 字串是由無符號的 16 位值 組成的序列,常見的 Unicode 字元都是通過 16 位內碼錶示的,如果出現 超出範圍 的字元,將會遵循 UTF-16 編碼規則,使用 兩個 16 位值 標識,然而 JavaScript 中對字串的操作都是 基於 16 位值。
// ? 遵循 UTF-16 規則,實際編碼為 '\ud83d\udc4b',在 JavaScript 中佔用兩個字元長度
var e = '?';
// 2 d83d dc4b
console.log(e.length, e.charCodeAt(0).toString(16), e.charCodeAt(1).toString(16));
// true ? ?
console.log(e === '\ud83d\udc4b', e, '\ud83d\udc4b');
複製程式碼
null 和 undefined
從語義上,null 代表 空值,undefined 代表 未定義、不存在,正常而言 null 可用來給變數賦值(清空變數等),而 很少使用 undefined 去給變數賦值。
全域性變數
宣告全域性變數時變數會 成為全域性物件的一個屬性。
// 定義全域性變數時 a 會直接掛載到 全域性物件下
var a = {};
// {} {} true
console.log(a, window.a, a === window.a);
複製程式碼