跳到主要内容

Object API

typeof object

因为 typeof null === "object" 成立,而 null 并不是对象。

function isObject(obj){
return Object.prototype.toString.call(obj) === "[object Object]";
}

Object.prototype.toString.call(null) 的结果是 [object Null]

或者(这种方式不好的一点是不能过滤掉Date、Array等类型的数据):

function isObject(obj){
return typeof obj === "object" && obj !== null;
}

引用类型

一个经典的例题:

var a = { n: 1 };   // 1
var b = a; // 2
a.x = a = { n: 2 }; // 3

console.log(a.x); // ?
console.log(b); // ?

答案:a.x 是 undefined;b 是 {n: 1, x: {n: 2}}

首先,a 是对象,是个引用类型,存储的指针指向 {n: 1} 这个对象。

第二行把指针 a 赋给 b,a 和 b 都指向 {n: 1}

第三行,首先会对 a.x 进行查找,没有找到就会先赋值 undefined,即:{n: 1, x: undefined}。此时 a 和 b 都指向同一个对象。然后 a 变量又赋值成一个新的对象:{n: 2},最后把新的 a 赋值给 x(前面的 a. 已经被替换成了原来的 a 所指向的那个内存中的对象),x 就有值了,b 就变成了:

{
n: 1,
x: { n: 2 }
}

这个题目最大的疑惑可能就是在第三行,a 变量的指向已经变了,而 a.x 会误认为新的 a 中有变量 x 并且它指向 a。事实上在执行第三行代码时,先处理了 a.x,让 x 先等于 undefined(属性访问优先级要比赋值优先级高),我觉得可以把此时的 a.x 看作是一个整体,它是旧的 a 指向的对象中的一个变量,正在等待赋值,当赋值时,这个变量指向 {n:2}

Object.is

考虑下面的代码输出结果为 true 还是 false?

console.log(Object.is(-0, +0));
console.log(Object.is(0, -0));
console.log(Object.is(0, +0));
console.log(Object.is(NaN, NaN));
console.log(Object.is(NaN, 0/0));
console.log(Object.is([],[]));

答案:false,false,true,true,true,true

💡 解析

Object.is() 判断两个值是否相同。如果下列任何一项成立,则两个值相同:

  • 两个值都是 undefined
  • 两个值都是 null
  • 两个值都是 true 或者都是 false
  • 两个值是由相同个数的字符按照相同的顺序组成的字符串
  • 两个值指向同一个对象
  • 两个值都是数字并且:
    • 都是正零 +0(没有符号的 0 相当于 +0)
    • 都是负零 -0
    • 都是 NaN
    • 都是除零和 NaN 外的其它同一个数字

可以看出,这种相等性判断逻辑和传统的 == 运算不同。== 运算符会对它两边的操作数做隐式类型转换(如果它们类型不同),然后才进行相等性比较,但 Object.is 不会做这种类型转换。

这与 === 运算符的判定方式也不一样。=== 运算符(和 == 运算符)将数字值 -0+0 视为相等,并认为 Number.NaN 不等于 NaN

上边例题中,Object.is(NaN, 0/0) 之所以为 true,是因为 0/0 等于 NaN。而 0/2 等于多少呢?2/0 又是等于多少呢?于是诞生出了下面几个问题:

console.log(Object.is(NaN, 0/2));       // false
console.log(Object.is(NaN, 2/0)); // false
console.log(Object.is(2/0, 0/2)); // false
console.log(Object.is(Infinity, 2/0)); // true
console.log(Object.is(Infinity, Infinity)); // true
console.log(Object.is(-Infinity, -Infinity)); // true

在 JavaScript 中,0 除以一个不为零的数时,如果这个数是正数,则结果为 +0;如果这个数是负数,则结果是 -0。当一个不为零的数除以 0 时,当这个数是负数时,则结果是 -Infinity,表示负无穷;当这个数是正数时,则结果是 Infinity,表示正无穷;

在 JavaScript 中,无论是 == 还是 ===Infinity 都等于自身,-Infinity 也是都等于自身。因此上面代码中,后三个结果是 true

拓展:如何实现这个方法?

Object.is = function(x, y){
// 如果两个值相等
if (x === y) {
// 让 -0 != +0 成立
return x !== 0 || 1 / x === 1 / y;
} else {
// 如果两个值不相等(如:NaN !== NaN)
// 让 NaN == NaN 成立
return x !== x && y !== y;
}
}

解释一下上面将 -0 != +0 成立的原理。加入参数 x 为 +0,y 为 -0,x !== 0 显然不成立(反过来也一样),然后看第二个条件,1/+0 等于 Infinity,1/-0 等于 -Infinity。因此也不行等,于是返回 false

特例: Object.is(0.2 + 0.1, 0.3); // false