笔记 09 JS 类型

原文地址
有趣的二维表 (运算符 == )

类型

1 相等与比较

JavaScript 有两种方式判断两个值是否相等。

1.1 == 等于操作符

JavaScript 是弱类型语言,这就意味着,等于操作符会为了比较两个值而进行强制类型转换。

/**
 * [1] == 被广泛认为是不好编程习惯的主要原因,
 * 由于它的复杂转换规则,会导致难以跟踪的问题
 *
 * [2] 强制类型转换也会带来性能消耗,
 * 比如一个字符串为了和一个数字进行比较,必须事先被强制转换为数字
 */
""           ==   "0"           // false
0            ==   ""            // true
0            ==   "0"           // true
false        ==   "false"       // false
false        ==   "0"           // true
false        ==   undefined     // false
false        ==   null          // false
null         ==   undefined     // true
" \t\r\n"    ==   0             // true

1.2 === 严格等于操作符

不像普通的等于操作符,严格等于操作符不会进行强制类型转换。

// 两个操作数类型不同就肯定不相等也有助于性能的提升
""           ===   "0"           // false
0            ===   ""            // false
0            ===   "0"           // false
false        ===   "false"       // false
false        ===   "0"           // false
false        ===   undefined     // false
false        ===   null          // false
null         ===   undefined     // false
" \t\r\n"    ===   0             // false

1.3 比较对象

虽然 == 和 === 操作符都是等于操作符,
但是当其中[有一个操作数为对象]时,行为就不同了。

{} === {};                   // false
new String('foo') === 'foo'; // false
new Number(10) === 10;       // false
var foo = {};
foo === foo;                 // true

这里等于操作符比较的不是值是否相等,而是是否属于同一个身份;
也就是说,只有对象的同一个实例才被认为是相等的。
这有点像 Python 中的 is 和 C 中的指针比较 or 引用比较

[注]
如果类型需要转换,
应该在比较之前显式的转换,
而不是使用语言本身复杂的强制转换规则。

2 typeof 操作符

typeof 操作符(和 instanceof 一起)或许是
JavaScript 中最大的设计缺陷,
因为几乎不可能从它们那里得到想要的结果。

2.1 JavaScript 类型表格

// Type 一列表示 typeof 操作符的运算结果
// Class 一列表示对象的内部属性 [[Class]] 的值

// 为了获取对象的 [[Class]],
// 我们需要使用定义在 Object.prototype 上的方法 toString。

Value               Class      Type
-------------------------------------
"foo"               String     string
new String("foo")   String     object
1.2                 Number     number
new Number(1.2)     Number     object
true                Boolean    boolean
new Boolean(true)   Boolean    object
new Date()          Date       object
new Error()         Error      object
[1,2,3]             Array      object
new Array(1, 2, 3)  Array      object
new Function("")    Function   function
/abc/g              RegExp     object (function in Nitro/V8)
new RegExp("meow")  RegExp     object (function in Nitro/V8)
{}                  Object     object
new Object()        Object     object

2.2 对象的类定义

JavaScript 标准文档只给出了一种获取 [[Class]] 值的方法,
那就是使用 Object.prototype.toString。

[].slice(start, end)

start:
   必需。规定从何处开始选取。
   如果是负数,那么它规定从数组尾部开始算起的位置。
   也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。
end:
   可选。规定从何处结束选取。
   该参数是数组片断结束处的数组下标。
   如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。
   如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。
/**
 * Object.prototype.toString 返回一种标准格式字符串,
 * 可以通过 slice 截取指定位置的字符串,如下所示:
 */
Object.prototype.toString.call([])    // "[object Array]"
Object.prototype.toString.call({})    // "[object Object]"
Object.prototype.toString.call(2)    // "[object Number]"

function is(type, obj) {
    var clas = Object.prototype.toString.call(obj).slice(8, -1);
    return obj !== undefined && obj !== null && clas === type;
}

is('String', 'test'); // true
is('String', new String('test')); // true

// ======= 以下可以忽略了
// IE8
Object.prototype.toString.call(null)    // "[object Object]"
Object.prototype.toString.call(undefined)    // "[object Object]"

// Firefox 4
Object.prototype.toString.call(null)    // "[object Null]"
Object.prototype.toString.call(undefined)    // "[object Undefined]"

2.3 测试未定义变量

// 代码会检测 foo 是否已经定义;
// 如果没有定义而直接使用会导致 ReferenceError 的异常。
// 这是 typeof 唯一有用的地方。
typeof foo !== 'undefined'

[注]

为了检测一个对象的类型,
强烈推荐使用 Object.prototype.toString 方法;
因为这是唯一一个可依赖的方式。
正如上面表格所示,
typeof 的一些返回值在标准文档中并未定义,
因此不同的引擎实现可能不同。
除非为了检测一个变量是否已经定义,
我们应尽量避免使用 typeof 操作符。