js中[] == ![] 发生了什么?

更新日期: 2019-07-29阅读: 2.1k标签: 类型

记不清在某处看见了这一比较,当时对强制转换这块理解的还没有特别清晰,故有此一文。以为我会以标题的表达式来展开?那你就错了,下面直接上[] == []是如何转换的:

因为!运算符的优先级比较高,所以表达式右侧先运行[],得出false,表达式变为[] == false
强制将false转换为0,表达式变为[] == 0
将[]强制转换为原始类型后为"",表达式变为"" == 0
将""转换为Number类型,表达式变为0 == 0
两侧类型相同,直接返回0 === 0的结果true


前言

本文旨在总结js中强制转换的规则及触发强制转换的几种场景。ES6标准中定义了六种原始类型,分别是Undefined,Null,String,Number,Boolean,Symbol。本文中的强制转换指的是在代码运行时,触发了数值的隐式转换,而不是代码显示的指定转换操作。


原始类型间强制转换

发生在原始类型之间的转换,以个人的理解是其他类型转换为String,Number或者Boolean类型。


转换为String类型

其他原始类型转换为String类型通常发生在+两边存在字符串时,会将+另一边的值转换为String类型。
考虑如下代码:

var strAddNum = "test" + 1;
var numAddStr = 1 + "test";
var boolAddStr = true + "test";
var undAddStr = undefined + "";
var nullAddStr = null + "";
console.log(strAddNum);
console.log(numAddStr);
console.log(boolAddStr);
console.log(undAddStr);
console.log(nullAddStr);

以上代码的运行结果均为字符串。其他原始类型转换为String类型基本是其值的字符串形式,具体如下:

    Undefined,"undefined"
    Null,"null"
    Boolean,"true"或"false"
    Number,值为NaN,"NaN"
    Number,值为+0或-0,"0"
    Number,值为+Infinity,"Infinity"
    Number,值为-Infinity,"-Infinity"

注意:Symbol类型无法转换为String类型。


转换为Number类型

转换为Number类型的情况,+-*/%等运算中,除了+之外其他运算均会转换成Number类型,+运算时需要满足两侧未出现String类型,该值才会被转换为Number类型。+运算时情况较为复杂,后面会专门描述其相关转换规则。考虑如下代码:

var trueAddTrue = true + true;
var trueAddFalse = true + false;
var trueAdda0 = true + 0;
var nullAddTrue = null + true;
var undefinedAdd0 = undefined + 0;
var strAdd0 = "" + 0;
console.log(trueAddTrue);
console.log(trueAddFalse);
console.log(trueAdda0);
console.log(nullAddTrue);
console.log(undefinedAdd0);
console.log(strAdd0);

在运行代码之前可以先考虑下以上代码答打印的结果分别是什么?然后再运行,看是否符合你的预期。其他原始类型转换为Number类型的具体如下:

    Undefined,NaN
    Null, +0
    Boolaen,值为true,1
    Boolean,值为false,+0
    String,不可转为Number的,NaN
    String,可转为Number的就是其对应的Number值

注意:Symbol类型同样无法转换为Number类型。


转换为Boolean类型

转换为Boolean类型的情况较为简单,除了以下情况转换为Boolean类型会是false,其他情况均是true

    Undefined
    Null
    Number,+0,-0,NaN
    String,长度为0的字符串
    这几种false的情况在ES标准中有明确规定


对象强制转换为原始类型

ES中将对象转换为原始类型的算法,大致可描述为三种情形:

如果该对象设置了[Symbol.toPrimitive],调用该函数,如果其返回值为非Object类型则返回结果,否则抛出TypeError异常
若未指定转换提示则转换提示为"default"
若转换提示为"default",则将其置为"number"
当指定转换提示为"number"时先调用该对象的valueOf函数并判断其结果,如果是原始类型则返回结果,否则调用该对象的toString函数并判断其返回结果,如果结果为原始类型则返回,否则抛出异常TypeError
当指定转换提示为"string"时先调用toString函数并判断其返回结果,如果是原始类型则返回结果,否则调用该对象的valueOf函数并判断其返回结果,如果结果为原始类型则返回,否则抛出异常TypeError

上述三种情形中第一种情形优先级最高,第二三种情形优先级并列,具体需要根据使用场景判断是哪一种。其中的指定转换提示是ES标准内部调用该算法时指定的。

第一种情形只有Symbol对象和Date对象内置了[Symbol.toPrimitive],且该属性的writeable为false,enumerable为false,configurable为true
对象转换为原始类型时发生的强制转换非特殊情况均为第二种,第三种情况较为少见。在正常编码工作中应该使用第二种情形就够用了,第三种情形几乎不会出现,要了解更多细节可查阅ES标准。

var test = {
  [Symbol.toPrimitive]: function(hint) {
     console.log(hint)
  },
  valueOf: function() {
      console.log("valueOf")
  },
  toString: function() {
      console.log("toString")
  }
}
test + "";  //"default"
test * 0;   //"number"
String(test);   //"string"

上述代码指定了分别指定了test对象的[Symbol.toPrimitive],valueOf和toString函数,可以观察到并valueoOf和toString函数均未被调用,指定的[Symbol.toPrimitive]函数可以接受一个提示参数,这个参数就是强制转换时的强制转换提示。这样我们在函数中就可以根据转换场景的不同分别返回不同的值。


原始类型强制转换为对象(装箱)

在开始描述这个问题之前,可以先思考一下,都有哪些场景会是强制的将原始类型转换为对象,其实这种场景几乎在js代码中随处可见,考虑如下代码:

var str = "testString";
str.replace("test", "");

如上代码中定义的str的值并不是一个对象而是一个原始类型String,原始类型显然是没有方法可以调用的。

实际上这里的str在执行str.replace时str其值会被强制转换为对象,得到一个String类型的实例对象,而该实例的原型上定义了一系列方法,且该实例是无法被获取的,在执行完这行代码后,该实例就会被回收,所以这里的str依然是一个字符串。

考虑如下代码:

var a = 3;
a.fn = function(){};
a.fn();


强制转换的几种场景

在js代码中会出现强制转换的场景通常有三种:

    +运算
    -,*,/,%运算
    ==比较

    作为判断条件

    +运算

    一元+运算

    做一元+运算时,均会被强制转为Number类型,例如

var a = {
    [Symbol.toPrimitive]: function(hint) {
        console.log(hint);  // number
        if(hint === "number") {
            return 2;
        } else {
            return 9;
        }
    }
};
console.log(+a);   // 2

var b = "3";
console.log(+b);    // 3


二元+运算

二元+运算为几种强制转换中复杂度仅次于==比较的一种情形,个人总结其转换步骤如下:

先将两侧数值强制转换为原始类型(未指定转换提示,即转换提示为hint default);
若两侧存在String类型,均转换为String类型,则返回两侧拼接的字符串;
若第2未返回,则两侧数值强制转换为Number类型,返回计算结果;
var a = "";
var b = {
    [Symbol.toPrimitive]: function(hint) {
        console.log(hint);  // "default"
        if(hint === "default") {
            return 2;
        } else {
            return 9;
        }
    }
};
var c = a + b;  //这里b转换为原始类型返回的是Number类型2,由于a是"",所以b被转换为"2",后与""拼接返回"2"
console.log(c); // "2"

var d = 3;
var e = {
    [Symbol.toPrimitive]: function(hint) {
        console.log(hint);  // "default"
        if(hint === "default") {
            return 2;
        } else {
            return 9;
        }
    }
};
var f = d + e;  //这里e转换为原始类型返回的是Number类型2,由于两侧均没有String类型,则至第3步,强制转换为Number后返回两侧相加的结果5
console.log(f); // 5


-,*,/,%运算

这几个运算符这涉及的强制转换都是转换为Number类型的,所以这里只要搞清楚转换为Number是怎样的过程就可以了。上文中已经对原始类型转换为Number类型做了描述,这里补充一下Object转换为Number的过程:

将对象转换为原始类型,且转换时会指定转换提示为"number";
转换为原始类型后再根据原始类型转换为Number类型进行转换;
var a = 8;
var b = {
    [Symbol.toPrimitive]: function(hint) {
        console.log(hint);  // "number"
        if(hint === "number") {
            return 2;
        } else {
            return 9;
        }
    }
};
console.log(a-b);   //  6
console.log(a/b);   // 4
console.log(a*b);   // 16
console.log(a%b);   // 0

console.log(undefined * 0);   //NaN
console.log(null * -1); // 0
console.log(false * -1);    //0 
console.log(true * -1); // -1
console.log("1" * -1);  // -1


==比较

==比较的基础===比较

x === y,其具体比较步骤如下:

若x和y的类型不一致,返回false;
若x和y为Number类型,则若x和y中有一个为NaN返回false;若x和y的值相等则返回true;若x是+0,y是-0或者x是-0,y是+0则返回true;其他情况返回false;
若x和y为Undefined类型,返回true
若x和y为Null类型,返回true
若x和y为String类型,其值相同则返回true,否则返回false
若x和y为Boolean类型,其值均为true或均为false返回true,否则返回false
若x和y为Symbol类型,其值为同一个Symbol值则返回true,否则返回false
若x和y为Object类型,其值为同一个对象(其引用地址相同)则返回true,否则返回false

x == y规则

==比较的转换规则虽然稍微多一点,实际上也就几条规则,两侧的数值类型符合哪种就按哪种去转换,只不过有的可能需要转两次,具体如下:

如果两侧类型相等则直接返回===的结果;
若x为undefined和y为null或x为null和y为undefined,返回true
若两侧为String类型和Number类型,将String类型转换为Number类型,继续用==比较
若有一侧存在Boolean类型,将Boolean类型转换为Number类型,继续用==比较
若两侧为String,Number或Symbol类型和Object类型,将Object类型转换原始类型,继续用==比较
其他返回false

下面列举一些可能有点违反直觉的比较

"0" == false; // true
false == 0; // true
false == ""; // true
false == []; // true
"" == 0; // true
"" == []; // true
0 == []; // true
[] == ![];  //true


作为条件判断

这种情形到没有太多可说的,基本上就是,除了undefined,null,+0,-0,NaN,""这六个值会被转为false,其他情况均为true;

出现将不是Boolean类型的值强制转换的情况为

if(...)
for(...;...;...)第二个条件表达式
while(...)和do...while(...)中的条件表达式
...?...:...三元表达式中的第一个条件表达式
||和&&


结论

其实上面描述了这么多,日常开发环境中用到比较多的应该是作为判断条件,+,==这三种情况了,这三种中最常见的应该是判断条件的情况了,这种情况反而是最简单的一种了。

原文来自:https://www.cnblogs.com/sujinqu/p/11421385.html

链接: https://fly63.com/article/detial/5027

JS中Null与Undefined的区别

在JavaScript中存在这样两种原始类型:Null与Undefined。这两种类型常常会使JavaScript的开发人员产生疑惑,在什么时候是Null,什么时候又是Undefined?Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。

Javascript的类型检测

主要介绍了JS中检测数据类型的几种方式,typeof运算符用于判断对象的类型,但是对于一些创建的对象,它们都会返回\'object\',有时我们需要判断该实例是否为某个对象的实例,那么这个时候需要用到instanceof运算符

js类型转换的各种玩法

对于object和number、string、boolean之间的转换关系,ToPrimitive是指转换为js内部的原始值,如果是非原始值则转为原始值,调用valueOf()和toString()来实现。

JavaScript类型:关于类型,有哪些你不知道的细节?

Undefined类型表示未定义,它的类型只有一个值为undefined。undefined和null有一定的表意差别。非整数的Number类型无法使用 == 或 === 来比较,因为 JS 是弱类型语言,所以类型转换发生非常频繁

为你的 JavaScript 项目添加智能提示和类型检查

近在做项目代码重构,其中有一个要求是为代码添加智能提示和类型检查。智能提示,英文为 IntelliSense,能为开发者提供代码智能补全、悬浮提示、跳转定义等功能,帮助其正确并且快速完成编码。

js的类型

基本类型:按值访问,可以操作保存在变量中实际的值;引用类型数据存在堆内存,而引用存在栈区,也就是说引用类型同时保存在栈区和堆区,关于==的执行机制,ECMASript有规范,因为==前后的值交换顺序,返回的值也是一样的,所以在此对规范做出如下总结

再也不用担心 JavaScript 的数据类型转换了

JavaScript 是一种弱类型或者说动态类型语言。所以你不用提前声明变量的类型,在程序运行时,类型会被自动确定,你也可以使用同一个变量保存不同类型的数据。

JavaScript基础之值传递和引用传递

js的值传递和引用(地址)传递:js的5种基本数据类型 number,string,null,undefined,boolean 在赋值传递时是值传递,js的引用数据类型(object,array,function)进行引用传递,其实底层都是对象。

JS中的布尔 数字 字符串

JS中所有的值都可以转换成布尔类型 使用Boolean()或者 !!(两个感叹号),JS中所有的值都可以转换成数字类型,使用Number()或+。数字类型转换场景目的只有一个,用于计算,将后台传递的数据,从字符串转换为数字并参与计算

if条件中,js的强制类型转换

众所周知,JS在很多情况下会进行强制类型转换,其中,最常见两种是:1.使用非严格相等进行比较,对==左边的值进行类型转换2.在if判断时,括号内的值进行类型转换,转化为布尔值

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!