笔记 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 操作符。