iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 5
0

這個章節主要是介紹各種 “值” 在使用時,需要注意的地方。

Tony 覺得主要的重點在

(上)

  • 陣列可以給他字串的 key,但不算在陣列的長度中。
  • 陣列是可變的,字串是不可變的。
  • number 都是浮點數。

(下)

  • 小數的計算要小心。
  • 非數值的定義和判斷 NaN, -0, Infinity。
  • call by value VS. call by referance。(這部分之後再補充)

小的十進位值

惡名昭彰的副作用。
只要使用 IEEE 754 的語言都會有,不是只有 JS。(真是追根究底,但讀起來有拖下水的感覺)

0.1 + 0.2 === 0.3; // false
0.1 + 0.2;         //0.30000000000000004

以二進位浮點數字表示的 0.1 和 0.2 並不精確。這算不上是臭蟲,是無法避免的。

但整數是 OK 的。如果浮點數要確實運用,請使用ES6 的 .EPSILON 的方法。(請在書上翻閱)

安全的整數範圍

就是肯定可以運算。安全的精準無誤的範圍。

2^53 -1。

9007199254740991 比九千兆大一點點。
ES6 有預先定義 Number.MAX_SAFE_INTERGER。
也定義了Number.MIN_SAFE_INTERGER,也就是 -(2^53-1)。

這種大數字一般是出現在處理資料庫的64位元 ID。64位元無法以 number 傳遞。所以必須要用 string 儲存和傳輸。

大數運算在未來的版本,可能會有支援。

測試整數

Number.Integer();

ES6 規格

Number.Integer(42);     // true
Number.Integer(42.000); // true
Number.Integer(42.3);   // false

非 ES6 規格,可以用這個方法,再加上 polyfill。

if (!Number.isInteger) {
    Number.isInteger = function(num) {
        return typeof num == "number" && num % 1 == 0;
    }
}

(小補充)
以下方法都可以看數字是否為浮點數。

var num = 42.3;
num % 1 != 0;      // true
~~num != num;      // true
num + ".0" != num; //true

(還有測試是否為安全整數,請看書)

32 位元(有號)整數

有些運算要用位元來處理。最多 32 位元。

可以用|0,來對整數做強制轉型。之所以可以將上限 53 位元轉成 32 位元,是因為|本來就是用於 32 位元,與 0 做運算就是逐位元 no-op (no operation) 運算。

1|1; // 2
2|1; // 3,因為 10 + 1 = 11,就是 3。

特殊值

非值

null 是關鍵字,不是識別字。你不能把它當作變數然後指定值給他。

undefined 是識別字。

undefined

將值設定給 undefined。(不建議)。

  • 全域下,非 strict 模式。可以用。
  • 區域下,不管是否 strict。 都可以用。

是朋友,就不會覆寫 undefined。

void operator

undefined 是內建的識別字,持有內建的 undefined 值。也可以用 void 運算子取得。

var a = 42;
console.log( void a, a ); // undefined 42

實務上,void 0、void 1 和 undefined 沒有差異。

當需要確保運算式沒有結果(即使他有副作用)。

function doSomething() {
    if (!APP.ready) {
        return void setTimeout( doSomething, 100);
    }
    
    var result;
    return result;
}

if (doSomething()) {
    // 立即處理下個任務
}

特殊數字

非數字的數字

NaN (not a number),無效的數字(invalid)、不合格的數字(failed)、壞掉的數字(bad)。

NaN 的型別是 number。

var a = 2 / "foo";     // NaN
typeof a === "number"; // true

NaN 不等於任何數,包含他自己。

var a = 2 / "foo"; // NaN

a == NaN;    // false
a === NaN;   // false
NaN !== NaN; // true

他是唯一一個非反身值(not reflexive)。

那要如何測試? 用內建的 isNaN 的方法。

但 isNaN() 有缺陷。

var a = 2 / "foo";
var b = "foo";
a; // NaN
b; // "foo"

window.isNaN(a); // true
window.isNaN(b); // true 他的確 not a number,但他不是 NaN 阿!

這個 Bug 存在 19 年了。
ES6 有替代的工具。Number.isNaN()
也可以用他的特性 NaN !== NaN 來辨別。

無限

var a = 1 / 0; // Infinity
var b = -1/ 0; // -Infinity

要到達無限的階段,除了要大於容許的最大值,還要超過(約整至最接近值 round to nearest)。

var a = Number.MAX_VALUE; // 1.7976931348623157e+308
a + a;                    // Infinity
a + Math.pow( 2, 970 );   // Infinity
a + Math.pow( 2, 969 );   //1.7976931348623157e+308

成為無限之後的運算,幾乎不能回頭,成為無限就是無限,除了下列:
無限除以無限呢? NaN。
有限的 number 除以 無限呢? 0
負的有限 除以 無限?

JavaScript 的世界裡,有 -0 的存在。

為什麼需要 -0?

在某些特定應用,例如動畫設計,數值可以表示移動速度,正負號可以表示方向。
當你有這樣的特殊需求,需要辨別 0 和 -0 的差異,再看下面的敘述。

var a = 0 / -3; // -0
var b = 0 * -3; // -0

加法和減法不會產生 -0。

如果將 -0 轉成字串。會轉成 "0"。

a; // -0
a.toString(); // "0"
a + "";       // "0"
String(a);    // "0"

JSON.stringify(a); // "0",JSON 也沒保留 

但如果是從字串 "-0" 轉回成數字。就是 -0。

+"-0";            // -0
Number("-0");     // -0
JSON.parse("-0"); // -0

字串化會變,數值化不會變。運算子分不出來。

var a = 0;     // 0
var b = 0 *-1; // -0

a == b;  // true
a === b; // true
a > b;   // false

若要更清楚的分辨 0 和 -0 的差異。

function isNegZero(n) {
    n = Number(n);
    return (n === 0) && (1/n === -Infinity);
    // 值是零,且,有負號。
}

(接下來有關於 NaN 和 -0 判斷在 ES6 一勞永逸地方式,以及他的 polyfill 在書上有詳細的解說。)

值 VS 參考

私心推薦:好想工作室 小龜 同學
學JS的心路歷程 Day4 - 參數的傳遞方式(上)
看完再看這章,會更有體悟。


JavaScript 沒有指標。

var a = 2;
var b = a;
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c;
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]

參考資料

  1. JavaScript如何判断参数为浮点型
  2. 你所不知道的 JS
  3. 學JS的心路歷程 Day4 - 參數的傳遞方式(上)

上一篇
Day4 - 值(上)
下一篇
Day6 - Native
系列文
你為什麼不問問神奇 JavaScript 呢?30

尚未有邦友留言

立即登入留言