Libx

JS运算符和类型转换

字数统计: 3,087阅读时长: 12 min
2017/11/19 Share

之前真的是小瞧了JS的运算符,再看《JS高级程序设计》的时候,甚至都没有深入的了解JS的逻辑运算符,只是走马观花的边跳边看。。今天晚上闲着没事,不想写代码,就想看看一些小框架的源码学习学习。看到了大量的关于运算符骚操作之后,发现原来逻辑表达式还能这么玩,羞愧不已,于是重新搬出红宝书,又查了一些文档,对逻辑表达式来稍微深入探讨一下。因为涉及了很多类型转换,所以对类型转换也来总结一下。(虽然一个===可以解决几乎所有问题。。但还是要了解一下的~)


逻辑表达式基本知识补充

  • 逻辑非 !

    逻辑非可以用于ECMAScript中任何值,无论用于任何值都会返回一个布尔值,也就是说,他会先操作数变为一个布尔值,之后对其取反。而同时使用!!则可以达到使用Boolean()的效果了

    • 如果一个操作数是一个对象,返回false;

    • 如果一个操作数是一个空字符串,返回false;

    • 如果一个操作数是一个非空字符串,返回false;

    • 如果一个操作数是一个数值0,返回true;

    • 如果一个操作数是任意的非零字符,返回false;

    • 如果一个操作数是null,返回true;

    • 如果一个操作数是NaN,返回true;

    • 如果一个操作数是undefined,返回true;

    • 能够转换为false的表达式有:
      • null;
      • NaN;
      • 0;
      • 空字符串(””);
      • undefined.

    (其实这些东西可以说是很好理解了,,不过还是系统的总结一下)

  • 逻辑与

    对于不是布尔值的情况则:

    • 如果第一个操作数是对象,则返回第二个数

    • 如果第二个操作数是对象,则只有在第一个操作数的求值结果为true的情况下才会返回该对象;

    • 如果第两个操作数都是对象,则返回第二个数操作数

    • 如果有一个操作数是null,则返回null

    • 如果有一个操作数是NaN,则返回第NaN

    • 如果第一个操作数是undefined,则返回undefined

    逻辑运算符操作对象遵循从左到右的顺序来判断,逻辑与操作符(&&)先判断第一个数,如果第一个数的逻辑判断是true,则还需判断第二个数,结果输出第二个操作数;同理,如果第一个数为false,则不用考虑第二个数了,直接输出第一个数的逻辑判断结果.

  • 逻辑或

对于不是布尔值的情况则:

  • 如果第一个操作数是对象,则返第一个操作数

  • 如果第一个操作数的求值结果为false,则返回第二个操作数

  • 如果两个操作数都是对象,则返回第一个操作数

  • 如果两个操作数是null,则返回null

  • 如果两个操作数是NaN,则返回NaN

  • 如果两个操作数是undefined,则返回undefined

逻辑或(||)的判断是如果第一个操作数的逻辑判断为true,则直接输出第一个操作数,不用再考虑第二个操作数;如果第一个操作数的逻辑判断为false,则还得去判断第二个操作数的逻辑。

尽管 && 和 || 运算符能够使用非Boolean值的操作数, 但它们依然被看作是Boolean操作符,因为它们的返回值总是能够被转换为Boolean值。

短路计算

由于逻辑表达式的运算的顺序是从左到右,也可以用以下规则进行”短路”计算:

false && (anything) 短路计算的结果为false.

true || (anything) 短路计算的结果为 true.

该规则确保这些计算的准确性. 注意如果上述表达式中的 anything 部分不能被计算的话, 两边都不会生效.还需要注意的是,上述表达式中的 anything 部分是任意的单个逻辑表达式(小括号中).


JS表达式中的类型转换

相信很多前端都会觉得==的设计可以说是很糟糕了,,
一直以来都在刻意避免使用== 只用===,就是因为其中的类型转换,但是并不理解其中的具体的原因

相等本来可以说是很简单的操作了,但是也是对于基本的字符串,数字,布尔值来说,一旦涉及到对象的比较,就复杂了,早期的es的相等和不等都会先进行类型转换.之后的解决方案是使用== and ===

先来看看红宝书是怎么具体的说的

== != & === !==

== !=

ECMAScript 中的相等操作符由两个等于号(==)表示,如果两个操作数相等,则返回 true。而不相等操作符由叹号后跟等于号(!=)表示,如果两个操作数不相等,则返回 true。这两个操作符都会
先转换操作数(通常称为强制转型),然后再比较它们的相等性。
在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:

  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为 0,而true 转换为 1;
  • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法,用得到的基本类
    型值按照前面的规则进行比较;
    这两个操作符在进行比较时则要遵循下列规则。
  • null 和 undefined 是相等的。
  • 要比较相等性之前,不能将 null 和 undefined 转换成其他任何值。
  • 如果有一个操作数是 NaN,则相等操作符返回 false,而不相等操作符返回 true。重要提示:
    即使两个操作数都是 NaN,相等操作符也返回 false;因为按照规则,NaN 不等于 NaN。
  • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,
    则相等操作符返回 true;否则,返回 false。

=== & !==

除了在比较之前不转换操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。全等操作符由 3 个等于号(===)表示,它只在两个操作数未经转换就相等的情况下返回 true.

记住:null == undefined 会返回 true,因为它们是类似的值;但 null === undefined 会返回 false,因为它们是不同类型的值


这里有一篇好文:
作者:林建入
链接:https://www.zhihu.com/question/20348948/answer/19601270
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

严格相等运算符

=== 被称为 Strict Equals Operator,假设有表达式 a === b,则它的实际运算过程如下

  1. 计算出表达式 a 的结果,并存入 lref 变量
  2. 将 GetValue(lref) 的结果存入 lval 变量
  3. 计算出表达式 b 的结果,并存入 rref 变量
  4. 将 GetValue(rref) 的结果存入 rval 变量
  5. 执行 Strict Equality Comparison 算法判断 rval === lval 并将结果直接返回

这里的 Strict Equality Comparison 算法很关键,假设要计算的是 x === y,则过程如下

  1. 如果 Type(x) 和 Type(y) 不同,返回 false
  2. 如果 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,返回 ture
    6. 返回 false
  3. 如果 Type(x) 为 String,则当且仅当 x 与 y 的字符序列完全相同(长度相等,每个位置上的字符相同)时返回 true,否则返回 false
  4. 如果 Type(x) 为 Boolean,则若 x 与 y 同为 true 或同为 false 时返回 true,否则返回 false
  5. 如果 x 和 y 引用的是同一个对象,返回 true,否则返回 false

相等运算符

== 的实现好了,当你明白了 === 的实现之后,我们再来看看 == 的实现,比较一下他们有何不同?
== 被称为 Equals Operator (注意看没有 Strict 了),

假设有表达式 a == b,则它的实际运算过程如下

  1. 计算出表达式 a 的结果,并存入 lref 变量
  2. 将 GetValue(lref) 的结果存入 lval 变量
  3. 计算出表达式 b 的结果,并存入 rref 变量
  4. 将 GetValue(rref) 的结果存入 rval 变量
  5. 执行 Abstract Equality Comparison 算法判断 rval == lval 并将结果直接返回

注意,其中的前 4 个步骤是和 === 完全相同的。唯独 5 不同。对于 === 来说,调用的是 Strict Equality Comparison 算法,但是 == 则调用的是 Abstract Equality Comparison 算法。虽然仅一词之差,但是却有质的不同,我们下面就来看看到底它是怎么实现的

假设要计算的是 x == y,Abstract Equality Comparison 计算的过程如下(很冗长,但是每个步骤都很简单)

  1. 如果 Type(x) 和 Type(y) 相同,则

    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,返回 ture
  4. 如果 Type(x) 为 Number 且 Type(y) 为 String,以 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 的类型不同,那么一定会返回 false。而 a == b 则要灵活得多。JavaScript 会尝试调整 a 和 b 的类型,例如若 a 为字符串 b 为数字,则将字符串先转化为数字再与 b 比较,等等。这在很多时候简化了程序员的代码量。

还有一篇:https://segmentfault.com/a/1190000006012804

JS逻辑运算符的一些骚操作

其实也不骚hhh,很实用的操作

//判断类型
function type(obj) {
return obj == null ? String(obj) :
[String.prototype.toString.call(obj)] || "object"
}

// 判断是否是 window对象(注意,w为小写)指当前的浏览器窗口,window对象的window属性指向自身。
// 即 window.window === window
function isWindow(obj)
{ return obj != null && obj == obj.window }
// 判断是否是数组或者对象数组
function likeArray(obj) {
var length = !!obj && 'length' in obj && obj.length,
type = $.type(obj)
return 'function' != type && !isWindow(obj) && (
'array' == type || length === 0 ||
(typeof length == 'number' && length > 0 && (length - 1) in obj)
)
}
//有没有要看晕了。。我也要晕了,,实际工程里要这样写的话,会被打死吧。。

还有一个非常优雅的

//形如:
//如果X则_x,如果y则_y,如果z则_z等等等
//比如一岁给1个苹果,且增长无规律
//将swictch或者链式ifelse改写
var res = (age==5 && 1) || (age==10 && 2) || (age==12 && 3) || (age==15 && 4) || 0;

CATALOG
  1. 1. 逻辑表达式基本知识补充
    1. 1.1. 短路计算
  2. 2. JS表达式中的类型转换
    1. 2.1. == != & === !==
      1. 2.1.1. == !=
      2. 2.1.2. === & !==
      3. 2.1.3. 严格相等运算符
    2. 2.2. 相等运算符
  3. 3. JS逻辑运算符的一些骚操作