1. 特殊类型

因为typeof null === "object"因此我们需要使用复合条件来检测 null 值的类型

var a = null;
(!a && typeof a === "object"); // true

还有一种情况: typeof function a(){ /* .. */ } === "function";这样看来,function(函数)也是 JavaScript 的一个内置类型。然而查阅规范就会知道, 它实际上是 object 的一个“子类型”。具体来说,函数是“可调用对象”,它有一个内部属性[[Call]],该属性使其可以被调用。

函数不仅是对象,还可以拥有属性。例如:

function a(b,c) {
  /* .. */
}
a.length; // 2

函数对象的 length 属性是其声明的参数的个数。

函数还有数组都属于Object的子类型。

2. typeof

typeof 运算符总是会返回一个字符串

typeof typeof 42; // "string"

typeof 42 首先返回字符串 “number”,然后 typeof “number” 返回 “string”。


3. 数组

如果字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理

var a = [ ];
a["13"] = 42;
a.length; // 14
使用 delete 运算符可以将单元从数组中删除,但是请注意,单元删除后,数 组的 length 属性并不会发生变化。


4. 数字

JavaScript 使用的是“双精度”格式(即 64 位二进制)。

Number.EPSILON 判断 0.1 + 0.2 和 0.3 是否相等呢?

Number.EPSILON 是 JavaScript 中的一个内置常量,它表示在浮点数运算中可接受的最小误差范围。

在 JavaScript 中,由于浮点数的存储方式和运算规则,会导致一些精度问题。例如,当两个浮点数非常接近时,它们可能在计算中产生微小的误差。Number.EPSILON 提供了一个标准的误差范围,用于比较浮点数的相等性。

你可以使用 Number.EPSILON 来判断两个浮点数是否相等,例如:

function isEqual(a, b) {
  return Math.abs(a - b) < Number.EPSILON;
}

console.log(isEqual(0.1 + 0.2, 0.3)); // true

在上面的例子中,由于浮点数运算的精度问题,0.1 + 0.2 的结果并不等于 0.3。但是通过使用 Number.EPSILON,我们可以判断它们是否在可接受的误差范围内,从而得到正确的比较结果。

总之,Number.EPSILON 是用于处理浮点数精度问题的一个常量,可以帮助我们进行更准确的浮点数比较。


5. 整数的安全范围

能够被“安全”呈现的最大整数是2^53 - 1,即9007199254740991,在ES6中被定义为 Number.MAX_SAFE_INTEGER。 最 小 整 数 是 -9007199254740991, 在 ES6 中 被 定 义 为 Number. MIN_SAFE_INTEGER。

有时 JavaScript 程序需要处理一些比较大的数字,如数据库中的 64 位 ID 等。由于 JavaScript 的数字类型无法精确呈现 64 位数值,所以必须将它们保存(转换)为字符串。


6. Number.isInteger() 整数检测

要检测一个值是否是整数,可以使用 ES6 中的 Number.isInteger(..) 方法

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

也可以为 ES6 之前的版本 polyfill Number.isInteger(..) 方法

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


7. NaN

NaN是一个特殊值,它和自身不相等,是唯一一个非自反(自反,reflexive,即x === x不 成立)的值。而 NaN != NaN 为 true
判断是否NAN

// 方法一
function hasNaN(x) {
  return x !== x;
}
// 方法二:es6
Number.isNaN(2);


8. 无穷数

任何数除以0都是没有意义的,在传统编译型语言(如 C)就会报错,然而在 JavaScript 中上例的结果为 Infinity(即 Number.POSITIVE_INfiNITY)

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


9. 0和-0

  • 加法和减法运算不会得到负零

  • -0转为字符串会变成0

  • 将-0字符串转为数字会变成0

JSON.stringify(-0) 返回 “0”,而 JSON.parse(“-0”) 返回 -0
function isNegZero(n) {
  n = Number( n );
  return (n === 0) && (1 / n === -Infinity);
}
isNegZero( -0 );
isNegZero( 0 / -3 );
isNegZero( 0 );
// true
// true
// false


10. Object.is() 处理特殊等式

如前所述,NaN 和 -0 在相等比较时的表现有些特别。由于 NaN 和自身不相等,所以必须使 用 ES6 中的 Number.isNaN(..)(或者 polyfill)。而 -0 等于 0(对于 === 也是如此,参见第 4 章),因此我们必须使用 isNegZero(..) 这样的工具函数。

ES6 中新加入了一个工具方法 Object.is(..) 来判断两个值是否绝对相等,可以用来处理 上述所有的特殊情况

var a = 2 / "foo";
var b = -3 * 0;
Object.is( a, NaN );
Object.is( b, -0 );
Object.is( b, 0 );
能使用 == 和 ===时就尽量不要使用 Object.is(..),因为前者效率更高、更为通用。Object.is(..) 主要用来处理那些特殊的相等比较。

11. 值和引用

由于引用指向的是值本身而非变量,所以一个引用无法更改另一个引用的指向

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]
// 然后
b = [4,5,6]; 
a; // [1,2,3] 
b; // [4,5,6]

b=[4,5,6] 并不影响 a 指向值 [1,2,3],除非 b 不是指向数组的引用,而是指向 a 的指针, 但在 JavaScript 中不存在这种情况!函数参数就经常让人产生这样的困惑

function foo(x) {
   x.push(4);
   x; // [1,2,3,4]
   // 然后
   x = [4, 5, 6];
   x.push(7);
   x; // [4,5,6,7]
 }
 var a = [1, 2, 3];
 foo(a);
 a; // 是[1,2,3,4],不是[4,5,6,7]

我们向函数传递 a 的时候,实际是将引用 a 的一个复本赋值给 x,而 a 仍然指向 [1,2,3]。在函数中我们可以通过引用x来更改数组的值(push(4)之后变为[1,2,3,4])。但x = [4,5,6] 并不影响 a 的指向,所以 a 仍然指向 [1,2,3,4]。

我们不能通过引用 x 来更改引用 a 的指向,只能更改 a 和 x 共同指向的值。

如果要将 a 的值变为 [4,5,6,7],必须更改 x 指向的数组,而不是为 x 赋值一个新的数组。

function foo(x) {
   x.push( 4 );
   x; // [1,2,3,4]
   // 然后
   x.length = 0; // 清空数组 x.push( 4, 5, 6, 7 );
   x; // [4,5,6,7]
 }

 var a = [1,2,3];  
 foo( a );
 a; // 是[4,5,6,7],不是[1,2,3,4]

从上例可以看出,x.length = 0和x.push(4,5,6,7)并没有创建一个新的数组,而是更改 了当前的数组。于是 a 指向的值变成了 [4,5,6,7]。

请记住:我们无法自行决定使用值复制还是引用复制,一切由值的类型来决定。

以上内容选摘自 `你不知道的JavaScript` ,内容部分有部分做删改,根据博主自己的理解进行了一些文案上的调整,基本意思不变,如果有内容误导,可通过邮箱通知博主加以改正,谢谢合作