JavaScript的关系运算,没有我原想的那么简单。等终于理清它的运算逻辑之后,我的头大了至少一圈。而if语句的真假判定逻辑本身不难,但要把它和关系运算联系起来,相信你会和我一样,到达崩溃边缘。不信,请跟我来。

JavaScript的关系运算包括比较运算和等值运算两种。其中,比较运算包括:<、<=、>、>=,等值运算包括:==、!=、===、!==。因为===和!==运算比较简单,不容易和其它运算发生混淆,所以这里就不再讨论了。

先看比较运算符。由于>、>=和<、<=的运算相似,这里只作<和<=的运算解析。至于>和>=运算,参考<和<=运算即可。

小于运算符 ( < )

产生式 RelationalExpression : RelationalExpression < ShiftExpression 按照下面的过程执行 :

  1. 令 lref 为解释执行 RelationalExpression 的结果 .
  2. 令 lval 为 GetValue(lref).
  3. 令 rref 为解释执行 ShiftExpression 的结果 .
  4. 令 rval 为 GetValue(rref).
  5. 令 r 为抽象关系比较算法 lval < rval( 参见 11.8.5) 的结果
  6. 如果 r 为 undefined,返回 false. 否则 , 返回 r.

摘自:http://yanhaijing.com/es5/#193

小于等于运算符 ( <= )

产生式 RelationalExpression : RelationalExpression <= ShiftExpression 按照下面的过程执行 :

  1. 令 lref 为解释执行 RelationalExpression 的结果 .
  2. 令 lval 为 GetValue(lref).
  3. 令 rref 为解释执行 ShiftExpression 的结果 .
  4. 令 rval 为 GetValue(rref).
  5. 令 r 为为抽象关系比较算法 rval < lval( 参见 11.8.5) 的结果,参数 LeftFirst 设为 false
  6. 如果 r 为 true 或者 undefined ,返回 false. 否则 , 返回 true.

摘自:http://yanhaijing.com/es5/#195

观察上面两个运算符的执行过程,发现只有第5步和第6步不同。而对于产生式RelationalExpression来说,我们并不关注表达式RelationalExpression和ShiftExpression的求值方式,我们只想知道它是如何比较左值lval和右值rval并且得到什么结果,即第5、6步。而这两步中,最关键的就是抽象关系比较算法了。

抽象关系比较算法

以 x 和 y 为值进行小于比较(x<y 的比较),会产生的结果可为="" true,false或 undefined(这说明 x、y 中最少有一个操作数是 NaN)。除了 x 和 y,这个算法另外需要一个名为 LeftFirst 的布尔值标记作为参数。这个标记用于解析顺序的控制,因为操作数 x 和 y 在执行的时候会有潜在可见的副作用。LeftFirst 标志是必须的,因为 ECMAScript 规定了表达式是从左到右顺序执行的。LeftFirst 的默认值是 true,这表明在相关的表达式中,参数 x 出现在参数 y 之前。如果 LeftFirst 值是 false,情况会相反,操作数的执行必须是先 y 后 x。这样的一个小于比较的执行步骤如下:

  1. 如果 LeftFirst 标志是 true,那么
    1. 让 px 为调用 ToPrimitive(x, hint Number) 的结果。
    2. 让 py 为调用 ToPrimitive(y, hint Number) 的结果。
  2. 否则解释执行的顺序需要反转,从而保证从左到右的执行顺序
    1. 让 py 为调用 ToPrimitive(y, hint Number) 的结果。
    2. 让 px 为调用 ToPrimitive(x, hint Number) 的结果。
  3. 如果 Type(px) 和 Type(py) 得到的结果不都是 String 类型,那么
    1. 让 nx 为调用 ToNumber(px) 的结果。因为 px 和 py 都已经是基本数据类型(primitive values 也作原始值),其执行顺序并不重要。
    2. 让 ny 为调用 ToNumber(py) 的结果。
    3. 如果 nx 是 NaN,返回 undefined
    4. 如果 ny 是 NaN,返回 undefined
    5. 如果 nx 和 ny 的数字值相同,返回 false
    6. 如果 nx 是 +0 且 ny 是 -0,返回 flase
    7. 如果 nx 是 -0 且 ny 是 +0,返回 false
    8. 如果 nx 是 +∞,返回 fasle
    9. 如果 ny 是 +∞,返回 true
    10. 如果 ny 是 -∞,返回 flase
    11. 如果 nx 是 -∞,返回 true
    12. 如果 nx 数学上的值小于 ny 数学上的值(注意这些数学值都不能是无限的且不能都为 0),返回 ture。否则返回 false。
  4. 否则,px 和 py 都是 Strings 类型
    1. 如果 py 是 px 的一个前缀,返回 false。(当字符串 q 的值可以是字符串 p 和一个其他的字符串 r 拼接而成时,字符串 p 就是 q 的前缀。注意:任何字符串都是自己的前缀,因为 r 可能是空字符串。)
    2. 如果 px 是 py 的前缀,返回 true。
    3. 让 k 成为最小的非负整数,能使得在 px 字符串中位置 k 的字符与字符串 py 字符串中位置 k 的字符不相同。(这里必须有一个 k,使得互相都不是对方的前缀)
    4. 让 m 成为字符串 px 中位置 k 的字符的编码单元值。
    5. 让 n 成为字符串 py 中位置 k 的字符的编码单元值。
    6. 如果 n<m,返回 true。否则,返回 false。

注:使用或代替的时候要注意,这里的步骤 3 和加号操作符 + 算法 (11.6.1) 的步骤 7 的区别。

注:String 类型的比较使用了其编码单元值的作为一个简单的词法表序列去比较。这里不打算使用更复杂的、语义化的字符或字符串序列,和 Unicode 规范的整理序列进行比较。因此,字符串的值和其对应的 Unicode 标准的值是不相同的。实际上,这个算法假定了所有字符串已经是正常化的格式。同时要注意,对于字符串拼接追加的字符的时候,UTF-16 编码单元值的词法表序列是不同于代码点值的序列的。

摘自:http://yanhaijing.com/es5/#197

我不得不说,这规范一丝不苟,非常仔细,仔细到让人有些混乱。

LeftFirst标记和本文关系不大,可以跳过本段。文中提到,LeftFirst是必须的,因为ECMAScript规定了表达式是从左到右顺序执行的。举个例子,在小于等于运算符产生式 RelationalExpression : RelationalExpression <= ShiftExpression的第5步,会用抽象关系比较算法比较右值rval和左值lval,这时,右值参数rval在左值参数lval的左边,如果没有LeftFirst标记控制,就会先对右值参数rval作ToPrimitive等一系列操作,违背了从左到右顺序执行的规则。

上文简单来说,就是对x和y进行toPrimitive操作,得到结果px和py。如果px和py都是字符串,就按从左到右顺序依次比较相应位置字符的字符编码值,否则就对px和py执行toNumber操作,得到结果nx和ny。如果nx或者ny是NaN,则返回undefined,否则返回两数的数值比较结果。

这里,又出现两个陌生的操作:toPrimitive和toNumber。

ToPrimitive

ToPrimitive 运算符接受一个值,和一个可选的 期望类型 作参数。ToPrimitive 运算符把其值参数转换为非对象类型。如果对象有能力被转换为不止一种原语类型,可以使用可选的 期望类型 来暗示那个类型。根据下表完成转换:

ToPrimitive转换

输入类型 结果
Undefined 结果等于输入的参数(不转换)。
Null 结果等于输入的参数(不转换)。
Boolean 结果等于输入的参数(不转换)。
Number 结果等于输入的参数(不转换)。
String 结果等于输入的参数(不转换)。
Object 返回该对象的默认值。(调用该对象的内部方法[[DefaultValue]]一樣)。

摘自:http://yanhaijing.com/es5/#103

ToNumber

ToNumber 运算符根据下表将其参数转换为数值类型的值:

ToNumber转换

输入类型 结果
Undefined NaN
Null +0
Boolean 如果参数是 true,结果为 1。如果参数是 false,此结果为 +0。
Number 结果等于输入的参数(不转换)。
String 参见下文的文法和注释。
Object 应用下列步骤:
  1. 原始值 ToPrimitive( 输入参数 , 暗示 数值类型)。
  2. 返回 ToNumber( 原始值 )。

摘自:http://yanhaijing.com/es5/#105

对于toPrimitive操作,值得一提的是数组对象。数组的valueOf方法返回该数组的原始值,即对其进行toPrimitive操作的结果。我在ECMAScript5.1规范中简单搜索了一下,没有搜到明确的说明,但是我们还是可以知道对数组进行toPrimitive操作的结果类型的,就是打印“结果值+1+2”。根据打印结果得知,结果是各元素以逗号分隔的字符串。这一点是很重要的!

对于toNumber操作,需要说一下的是对字符串类型应用toNumber的情况。在转换的过程中,空白字符是可以忽略不计的,而且空串的转换结果是0。这一点也很重要!至于详细说明,可参阅:http://yanhaijing.com/es5/#106

到此为止,我们只是完成了JavaScript的比较运算。下面,来分析一下等值运算。别着急,快结束了。

对于等值运算,我们只需分析一下==即可,对==的结果取反后就是!=的结果。

The Equals Operator ( == )

产生式 EqualityExpression : EqualityExpression == RelationalExpression 按照下面的过程执行 :

  1. 令 lref 为解释执行 EqualityExpression 的结果 .
  2. 令 lval 为 GetValue(lref).
  3. 令 rref 为解释执行 RelationalExpression 的结果 .
  4. 令 rval 为 GetValue(rref).
  5. 返回作用抽象相等比较算法于 rval == lval( 参见 11.9.3) 的结果

摘自:http://yanhaijing.com/es5/#201

我们直接看第5步,这里涉及到了抽象相等比较算法。

抽象相等比较算法

比较运算x==y, 其中x y是值,产生true或者false。这样的比较按如下方式进行:

  1. 若Type(x)与Type(y)相同, 则若x为null且y为undefined, 返回true。
    1. 若Type(x)为Undefined, 返回true。
    2. 若Type(x)为Null, 返回true。
    3. 若Type(x)为Number, 则
      1. 若x为NaN, 返回false。
      2. 若y为NaN, 返回false。
      3. 若x与y为相等数值, 返回true。
      4. 若x 为 +0 且 y为−0, 返回true。
      5. 若x 为 −0 且 y为+0, 返回true。
      6. 返回false。
    4. 若Type(x)为String, 则当x和y为完全相同的字符序列(长度相等且相同字符在相同位置)时返回true。 否则, 返回false。
    5. 若Type(x)为Boolean, 当x和y为同为true或者同为false时返回true。 否则, 返回false。
    6. 当x和y为引用同一对象时返回true。否则,返回false。
  2. 若x为null且y为undefined, 返回true。
  3. 若x为undefined且y为null, 返回true。
  4. 若Type(x) 为 Number 且 Type(y)为String, 返回comparison x == ToNumber(y)的结果。
  5. 若Type(x) 为 String 且 Type(y)为Number,返回比较ToNumber(x) == y的结果。
  6. 若Type(x)为Boolean, 返回比较ToNumber(x) == y的结果。
  7. 若Type(y)为Boolean, 返回比较x == ToNumber(y)的结果。
  8. 若Type(x)为String或Number,且Type(y)为Object,返回比较x == ToPrimitive(y)的结果。
  9. 若Type(x)为Object且Type(y)为String或Number, 返回比较ToPrimitive(x) == y的结果。
  10. 返回false。

注:按以上相等之定义:

  • 字符串比较可以按这种方式强制执行: "" + a == "" + b
  • 数值比较可以按这种方式强制执行: +a == +b
  • 布尔值比较可以按这种方式强制执行: !a == !b

注:等值比较操作保证以下不变:

  • A != B等价于!(A==B)
  • A == B等价于B == A,除了A与B的执行顺序。

注:相等运算符不总是传递的。例如,两个不同的String对象,都表示相同的字符串值;==运算符认为每个String对象都与字符串值相等,但是两个字符串对象互不相等。例如:

  • new String("a") == "a""a" == new String("a")皆为true。
  • new String("a") == new String("a")为false。

注:字符串比较使用的方式是简单地检测字符编码单元序列是否相同。不会做更复杂的、基于语义的字符或者字符串相等的定义以及Unicode规范中定义的 collating order。所以Unicode标准中认为相等的String值可能被检测为不等。实际上这一算法认为两个字符串已经是经过规范化的形式。

摘自:http://yanhaijing.com/es5/#203

有句话叫做过尤不及,严格的过了头儿,也是不好的。这规范就有这毛病,凑合着看吧。

上文简单的说,就是如果x和y类型相同,等值比较比较简单,只需要注意两点:1、当x和y都是Number类型时,若x或者y是NaN,则返回false;2、当x和y都是Object对象时,需看两者是不是引用的同一对象。如果x和y类型不同,那就费点事了:1、undefined == null成立,除此之外,undefined或者null和其它类型数据比较都返回false;2、数字和字符串作等值比较时,需把字符串转换成数字后再作比较;3、布尔值在和其它数据作等值比较时,需把布尔值转换成数字后再做比较;4、对象类型数据在和其它数据作比较时,需先获取对象的原始值,然后再做比较;其它情况返回false。

好了,JavaScript的等值运算我们也分析完了。只差if语句了,加油!

if语句判定真假时需对表达式作toBoolean操作,参阅:http://yanhaijing.com/es5/#220

ToBoolean

ToBoolean 运算符根据下表将其参数转换为布尔值类型的值:

ToBoolean转换

输入类型 结果
Undefined false
Null false
Boolean 结果等于输入的参数(不转换)。
Number 如果参数是 +0, -0, 或 NaN,结果为 false ;否则结果为 true。
String 如果参数参数是空字符串(其长度为零),结果为 false,否则结果为 true。
Object true

摘自:http://yanhaijing.com/es5/#104

这个没什么好说的,符合程序员的逻辑思维。如果在你的思维里不是这样的,那么只需要记住就行了。

到现在为止,终于可以画上句号了。如果你的大脑还没有崩溃,那么,请继续做一做下面的题吧,看你能对几个。


  1. undefined <= 0, undefined == 0
  2. null <= 0, null == 0
  3. undefined <= null, undefined == null
  4. '' <= 0, '' == 0, if ('')
  5. ' ' <= 0, ' ' == 0, if (' ')
  6. '0' <= 0, '0' == 0
  7. NaN <= 0, NaN == 0, NaN >= 0
  8. '2' <= true, '2' == true
  9. '0' <= false, '0' == false, if ('0')
  10. [] == 0, [0] == 0, ['0'] == 0
  11. if ([]), if ([0])
  12. {} >= 0, {} == 0

答案:

  1. false, false
  2. true, false
  3. false, true
  4. true, true, false
  5. true, true, true
  6. true, true
  7. false, false, false
  8. false, false
  9. true, true, true
  10. true, true, true
  11. true, true
  12. false, false

好了,我的大脑已经崩溃,剩下的就靠你们自己了。如果你们还有精力,建议你们再去分析一下PHP的相关逻辑。相信你们!

【JavaScript】深入分析JavaScript的关系运算和if语句的更多相关文章

  1. JavaScript学习笔记(2)——JavaScript和DOM的关系

    文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口.DOM实际上是以面向对象方式描述的文档模型.DOM定义了表示和修改文档所需的 ...

  2. JAVA-JAVA、JavaScript、JavaWeb的关系

    相关资料:1.http://www.cnblogs.com/youring2/archive/2013/06/06/3120645.html2.https://jingyan.baidu.com/ar ...

  3. 初识 Javascript.01 -- Javascript基础|输出方式、变量、变量命名规范、数据类型、

    Javascript基础 1 聊聊Javascript 1.1 Javascript的历史来源 94年网景公司   研发出世界上第一款浏览器. 95年 sun公司   java语言诞生 网景公司和su ...

  4. 前端JavaScript(1) --Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏

    一.Javascript简介 Web前端有三层: HTML:从语义的角度,描述页面结构 CSS:从审美的角度,描述样式(美化页面) JavaScript:从交互的角度,描述行为(提升用户体验) Jav ...

  5. Python学习教程(learning Python)--3.3.2 Python的关系运算

    如果if的condition不用布尔表达式来做条件判断而采用关系表达式,实际上关系表达式运算的结果要么是True要么是False.下面我们先了解一些有关关系运算符的基础知识,如下表所示. 做个小程序测 ...

  6. Python学习入门基础教程(learning Python)--3.3.2 Python的关系运算

    如果if的condition不用布尔表达式来做条件判断而采用关系表达式,实际上关系表达式运算的结果要么是True要么是False.下面我们先了解一些有关关系运算符的基础知识,如下表所示. 做个小程序测 ...

  7. JavaScript 对象JavaScript 对象

    JavaScript 中的所有事物都是对象:字符串.数值.数组.函数... 此外,JavaScript 允许自定义对象. 所有事物都是对象 JavaScript 提供多个内建对象,比如 String. ...

  8. JavaScript介绍-javaScript学习之旅(一)

    javaScript简介 1.javaScript是互联网上最流行的脚本语言,这门可用于web和html,更可广泛用于服务器端,pc端,移动端. 2.javaScript脚本语言: javaScrip ...

  9. 网页三剑客:HTML+CSS+JavaScript 之JavaScript

    JavaScript 简介 JavaScript 是互联网上最流行的脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务器.PC.笔记本电脑.平板电脑和智能手机等设备. JavaScrip ...

随机推荐

  1. windows 7下安装python+mongodb

    1. python安装 下载:http://python.org/download/ 直接双击安装,安装完后将路径加入系统环境变量path中. 2. mongodb安装 下载:http://www.m ...

  2. 动态内存分配(new)和释放(delete)

    在之前我们所写过的程序中,所必需的内存空间的大小都是在程序执行之前就已经确定了.但如果我们需要内存大小为一个变量,其数值只有在程序运行时 (runtime)才能确定,例如有些情况下我们需要根据用户输入 ...

  3. 『重构--改善既有代码的设计』读书笔记----Split Temporary Variable

    继续开始我们重构手法的系列,今天介绍的是Split Temporary Variable---分解临时变量. 在我们平常写的程序中肯定有某些临时变量被赋予了超过一个的责任.如果他们不是那种收集结果(t ...

  4. 深入浅出理解QTimeLine类

    网上找了下QTimeLIne类的介绍,要么就是代码一贴自己看去,要么就是说不到重点,正巧自己项目遇到这个类,在这里写一下,给需要的同学看下. 因为我最近需要有动画方面配合时间间隔触发QGraphics ...

  5. seo小技巧(转载)

    转载自前端网:五行缺火 优化技巧是老师在课堂上教不了你的,而自己也不可能在练习中领悟,最便捷的方法就是听取别人的经验,所以转载一下 SEO要点:1.语义化html标签,用合适的标签嵌套合适的内容,不可 ...

  6. layer弹出标签层tab

    引入文件: <script type="text/javascript" src="layer/layer.min.js"></script& ...

  7. (C初学) 对数组与指针的一些浅显的理解

    因为课堂上没听懂,又看不懂教科书(<C语言程序设计教程>第3版 谭浩强,张基温编著)上晦涩的表达方式,昨天晚上特意拿<C语言入门经典>这本书自己研究了一晚的数组与指针. 先来一 ...

  8. UVA - 12627 Erratic Expansion 奇怪的气球膨胀 (分治)

    紫书例题p245 Piotr found a magical box in heaven. Its magic power is that if you place any red balloon i ...

  9. spring mvc 自定义Handlermapping

    上次大概写了个可以解决velocity 多视图的东西. 但是实际运用过程中又到处找了些资料看了下.这里 小计下: DispatcherServlet解析过程: ..1..HandlerMapping. ...

  10. python中快速删除实例对象中的所有属性

    def DeleteObjectAllProperties(objectInstance): if not objectInstance: return listPro =[key for key i ...