-
Notifications
You must be signed in to change notification settings - Fork 840
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
从 []==![] 为 true 来剖析 JavaScript 各种蛋疼的类型转换 #1
Comments
写的不错~~get到了 |
我选择狗带 |
这次算是真的弄明白了,原来都似懂非懂 // valueOf
let obj = { valueOf: () => 1 }
obj == true // true
// toString
obj = { valueOf: () => ({}), toString: () => '' }
obj == false // true |
数形结合,艺术创作阿 |
不错 |
为什么{} ==![] 语法错误 |
@lyz1991 |
@lyz1991 你应该是在浏览器的命令行直接输入的吧?如果是这样的话,浏览器会将 {} 当成一段空语句,执行的是 == ![] 所以会报语法错误,你加个括号,({} == ![]) 就会发现结果为 false |
@mqyqingfeng |
太棒了 |
写的很好!俺表示看不懂 |
那你咋知道写的很好呢😂 |
除了堆和栈能够理解,其他的还真是N脸懵逼 |
看了五遍,同时还看了参考文章和文献,终于明白了,我是猪吗 |
跪了,辛苦前端同学了 |
a == 1 && a == 2 && a == 3 输出true方法 var a = {};
var num = 0;
a.valueOf = function(){
return ++num
};
a == '1' && a == '2' && a == '3' |
var obj = {}; |
那为什么 {} == !{} 不报错? |
@stone1314 |
类型转换看了n多篇,只有这里把事情的来龙去脉写完整了,必须赞一个 |
写的真不错 |
方便大家查阅,本文已首发在segmentfault社区,已上热门,谢谢大家支持.点我查阅
你是否在面试中遇到过各种奇葩和比较细节的问题?
看了这种题目,是不是想抽面试官几耳光呢?哈哈,是不是看了之后一脸懵逼,两脸茫然呢?心想这什么玩意
其实这些都是纸老虎,知道原理和转换规则,理解明白这些很容易的,炒鸡容易的,真的一点都不难,我们要打到一切纸老虎,不信?
我们就从[] == []和[] == ![]例子切入分析一下为什么输出的结果是true而不是其它的呢?
[]==[]为什么是false?
有点js基础应该知道对象是引用类型,就会一眼看出来[] == []会输出false,因为左边的[]和右边的[]看起来长的一样,但是他们引用的地址并不同,这个是同一类型的比较,所以相对没那么麻烦,暂时不理解[] == []为false的童鞋这里就不细说,想要弄清楚可以通过这篇文章来了解JavaScript的内存空间详解.
前端基础进阶(一):内存空间详细图解
变量对象与堆内存
那为什么引用值要放在堆中,而原始值要放在栈中,不都是在内存中吗,为什么不放在一起呢?那接下来,让我们来探索问题的答案!(扯远了)
首先,我们来看一下代码:
然后我们来看一下内存分析图:
变量num,bol,str为基本数据类型,它们的值,直接存放在栈中,obj,person,arr为复合数据类型,他们的引用变量存储在栈中,指向于存储在堆中的实际对象。 由上图可知,我们无法直接操纵堆中的数据,也就是说我们无法直接操纵对象,但我们可以通过栈中对对象的引用来操作对象,就像我们通过遥控机操作电视机一样,区别在于这个电视机本身并没有控制按钮。
现在让我们来回答为什么引用值要放在堆中,而原始值要放在栈中的问题:
记住一句话:能量是守衡的,无非是时间换空间,空间换时间的问题 堆比栈大,栈比堆的运算速度快,对象是一个复杂的结构,并且可以自由扩展,如:数组可以无限扩充,对象可以自由添加属性。将他们放在堆中是为了不影响栈的效率。而是通过引用的方式查找到堆中的实际对象再进行操作。相对于简单数据类型而言,简单数据类型就比较稳定,并且它只占据很小的内存。不将简单数据类型放在堆是因为通过引用到堆中查找实际对象是要花费时间的,而这个综合成本远大于直接从栈中取得实际值的成本。所以简单数据类型的值直接存放在栈中。
搬运文章:理解js内存分配
知道的大神当然可以飘过,这里主要给计算机基础薄弱的童鞋补一下课.
[]==![]为什么是true?
首先第一步:你要明白ECMAScript规范里面==的真正含义
GetValue 会获取一个子表达式的值(消除掉左值引用),在表达式 [] == ![] 中,[] 的结果就是一个空数组的引用(上文已经介绍到数组是引用类型),而 ![] 就有意思了,它会按照 11.4.9 和 9.2 节的要求得到 false。
首先我们了解一下运算符的优先级:
刚看到这里是不是就咬牙切齿了,好戏还在后头呢,哈哈
!取反运算符的优先级会高于==,所以我们先看看!在ECAMScript是怎么定义的?
所以![]最后会是一个Boolean类型的值(这点很关键,涉及到下面的匹配选择).
两者比较的行为在 11.9.3 节里,所以翻到 11.9.3:
在这段算法里,[]是Object,![]是Boolean,两者的类型不同,y是Boolean类型,由此可知[] == ![]匹配的是条件 8,所以会递归地调用[] == ToNumber(Boolean)进行比较。
[]空数组转化成Boolean,那么这个结果到底是true还是false呢,这个当然不是你说了算,也不是我说了算,ECMAScript定义的规范说了算:我们来看看规范:
[]是一个对象,所以对应转换成Boolean对象的值为
true
;那么**![]对应的Boolean值就是false
进而就成了比较[] == ToNumber(false)**了
再来看看ECMAScipt规范中对于Number的转换
由此可以得出此时的比较成了**[]==0**;
在此处因为 [] 是对象,0是数字Number,比较过程走分支 10(若Type(x)为Object且Type(y)为String或Number, 返回比较ToPrimitive(x) == y的结果。),可以对比上面那张图.
ToPrimitive
默认是调用toString
方法的(依 8.2.8),于是 ToPrimitice([]) 等于空字符串。再来看看ECMAScript标准怎么定义ToPrimitice方法的:
是不是看了这个定义,还是一脸懵逼,ToPrimitive这尼玛什么玩意啊?这不是等于没说吗?
再来看看火狐MDN上面文档的介绍:
JS::ToPrimitive
查了一下资料,上面要说的可以概括成:
首先我们要明白**obj.valueOf()**和 **obj.toString()**还有原始值分别是什么意思,这是弄懂上面描述的前提之一:
toString用来返回对象的字符串表示。
valueOf方法返回对象的原始值,可能是字符串、数值或bool值等,看具体的对象。
原始值指的是['Null','Undefined','String','Boolean','Number']五种基本数据类型之一(我猜的,查了一下
确实是这样的)
弄清楚这些以后,举个简单的例子:
如果觉得描述还不好明白,一大堆描述晦涩又难懂,我们用代码说话:
分析了这么多,刚才分析到哪里了,好像到了比较ToPrimitive([]) == 0现在我们知道ToPrimitive([])="",也就是空字符串;
那么最后就变成了**""==0**这种状态,继续看和比较这张图
发现typeof("")为string,0为number,发现第5条满足规则,最后就成了toNumber("")==0的比较了,根据toNumber的转换规则:
所以toNumber("")=0,最后也就成了0 == 0的问题,于是[]==![]最后成了0 == 0的问题,答案显而易见为true,一波三折
最后总结一下
==运算规则的图形化表示
主要参考文章和文献
ECMAScript5.1规范中文版
通过一张简单的图,让你彻底地、永久地搞懂JS的==运算
JavaScript中加号运算符的类型转换优先级是什么?
喜欢我总结的文章对你有帮助有收获的话麻烦点个star
我的github博客地址,总结的第一篇,不好之处和借鉴不得到之处还望见谅,您的支持就是我的动力!
The text was updated successfully, but these errors were encountered: