Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[] == []? [] == ![]? #33

Open
wengjq opened this issue Mar 4, 2018 · 1 comment
Open

[] == []? [] == ![]? #33

wengjq opened this issue Mar 4, 2018 · 1 comment

Comments

@wengjq
Copy link
Owner

wengjq commented Mar 4, 2018

1、[]==[] ?

javaScript 的变量类型

  • 基本类型:
    5种基本数据类型 Undefined、Null、Boolean、Number 和 String,变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。

  • 引用类型:
    存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

如下代码:

// 简单类型都放在栈(stack)里
// 对象类型都放在堆(heap)里
var a = 20;
var b = 'abc';
var c = true;
var d = {m: 20} // 地址假设为0x0012ff7c
var e = {m: 20} // 重新开辟一段内存空间假设为0x0012ff8f
console.log(e == d); // false

内存分布如下图:
687474703a2f2f7777312e73696e61696d672e636e2f6c617267652f6136363063616232677931666379697775697534636a3230796730697a616261

如上图可以得出 [] 左右两边的引用的地址是不同的,所以我们暂时可以得出 false 的答案。为了以防万一,可以查下规范。

tim 20180304155731

因为我们知道左边 [] 与右边 [] 不是同一个对象,所以确认 [] == [] 是 false 。

2、[] == ![] ?

javaScript 运算符的优先级

tim 20180304161522
我们通过查询 MDN 得知 ! 取反运算符的优先级会高于 == ,所以我们先看看 ! 取反运算符 在 ECAMScript 是怎么定义的:

tim 20180304162541

所以 ![] 最后会是一个 Boolean 类型的值(这点很关键,涉及到下面的匹配选择)。

[] 空数组转化成 Boolean ,那么这个结果到底是 true 还是 false 呢,这个当然不是你说了算,也不是我说了算,ECMAScript 定义的规范说了算,我们来看看规范,如下图:

tim 20180304163637

所以通过以上分析我们可以得到,原来的 [] == ![] 等价于 [] == false

接着,由上图的抽象相等比较算法第八点,若 Type(y) 为 Boolean, 返回比较 x == ToNumber(y) 的结果

进而就成了比较 [] == ToNumber(false) 了。

再来看看 ECMAScipt 规范中对于 Number 的转换。如下图:
tim 20180304164849

由此可以得出此时的比较成了 [] == 0

在此处因为 [] 是对象,0 是数字 Number,比较过程走分支 10(若 Type(x) 为 Object 且 Type(y) 为 String或 Number, 返回比较 ToPrimitive(x) == y 的结果。),可以对比上面那张图。

所以如上结果又可以转化成 ToPrimitive([]) == 0

再来看看 ECMAScript 标准怎么定义 ToPrimitice 方法的:

tim 20180304170057

tim 20180304170151

上面两个图可能有点难以理解,查下资料,可以概括为:

ToPrimitive(obj, preferredType);

JS 引擎内部转换为原始值 ToPrimitive(obj, preferredType) 函数接受两个参数,第一个 obj 为被转换的对象,第二个
preferredType 为希望转换成的类型(默认为空,接受的值为 Number 或 String )。

在执行 ToPrimitive(obj, preferredType) 时如果第二个参数为空并且 obj 为 Date 的事例时,此时 preferredType 会
被设置为 String ,其他情况下 preferredType 都会被设置为 Number。

如果 preferredType 为 Number,ToPrimitive 执行过程如下:
1. 如果 obj 为原始值,直接返回;
2. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
3. 否则调用 obj.toString(),如果执行结果是原始值,返回之;
4. 否则抛异常。

如果 preferredType 为 String ,将上面的第 2 步和第 3 步调换,即:
1. 如果 obj 为原始值,直接返回;
2. 否则调用 obj.toString(),如果执行结果是原始值,返回之;
3. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
4. 否则抛异常。

首先我们要明白 obj.valueOf()obj.toString() 还有原始值分别是什么意思,这是弄懂上面描述的前提:

toString 用来返回对象的字符串表示。

var obj = {};
console.log(obj.toString()); // [object Object]

var arr2 = [];
console.log(arr2.toString()); // ""空字符串
  
var date = new Date();
console.log(date.toString()); // Sun Mar 04 2018 17:10:11 GMT+0800 (中国标准时间)

valueOf 方法返回对象的原始值,可能是字符串、数值或 boolean 值等,看具体的对象。

var obj = {
  name: "obj"
};
console.log(obj.valueOf()); //Object {name: "obj"}

var arr1 = [1];
console.log(arr1.valueOf()); // [1]

var date = new Date();
console.log(date.valueOf()); // 1520154692221
// 如代码所示,三个不同的对象实例调用 valueOf 返回不同的数据

原始值指的是 ['Null', 'Undefined', 'String', 'Boolean', 'Number'] 五种基本数据类型之一。

分析了这么多,刚才分析到了比较 ToPrimitive([]) == 0 , 现在我们知道 ToPrimitive([])="" ,也就是空字符串。

那么最后就变成了 "" == 0 这种状态,继续看和比较下面这张图:

tim 20180304171943

发现 typeof("") 为 string,0 为 number ,发现第 5 条满足规则,最后就成了 toNumber("") == 0 的比较了,根据 toNumber 的转换规则:

tim 20180304172301

所以 toNumber("") = 0,最后也就成了 0 == 0 的问题,于是 [] == ![] 最后成了 0 == 0 的问题,答案显而易见为 true 。

3、== 运算规则的图形化表示

如下图:
20160723214407674510

前面说得很乱,根据我们得到的最终的图 ,我们总结一下 == 运算的规则:

1. undefined == null,结果是true。且它俩与所有其他值比较的结果都是 false 。

2. String == Boolean,需要两个操作数同时转为 Number。

3. String/Boolean == Number,需要 String/Boolean 转为 Number。

4. Object == Primitive,需要 Object 转为 Primitive (具体通过 valueOf 和 toString 方法)。

4、总结

建议尽量不要使用相等运算符。

5、以下为一些例子

// valueOf
let obj = { valueOf: () => 1 }
obj == true // true

// toString
obj = { valueOf: () => ({}), toString: () => '' }
obj == false // true

[] == false // true

!![] // true

++[[]][+[]]+[+[]] == 10 ?

6、参考

jawil/blog#1

@zhoubhin
Copy link

哈哈,[] == [] 返回false我是直接根据引用的内存地址不同而得出判断的,看了文章明白[] == ![] 返回true的原因了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants