将值从一种类型转换为另一种类型通常称为类型转换,这是显式的情况,隐式的情况称为强制类型转换。

JavaScript中的强制类型转换总是返回标量基本类型值,如字符串、数字和布尔值,不会返回对象和函数。

类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时。

我们能够从代码中看出哪些地方是显式强制类型转换,而隐式强制类型转换则不那么明显,通常是某些操作产生的副作用。

1.字符串、数字和布尔值之间类型转换的基本规则

1.1 ToString

基本类型值的字符串化规则为:null转换为"null",undefined转换为"undefined",true转换为"true"。

对于普通对象来说,除非自定义,否则toString()返回内部属性[[Class]]的值,如"[object Object]"。

  1. var a = [1,2,3];
  2. console.log(a.toString());//"1,2,3"

toString()可以被显式调用,或者在需要字符串化时自动调用。

JSON.stringify(..)在将JSON对象序列化为字符串时也用到了ToString。

  1. JSON.stringify(42);//"42"
  2. JSON.stringify("42");//""42""
  3. JSON.stringify(null);//"null"
  4. JSON.stringify(true);//"true"

所有安全的JSON值都可以使用JSON.stringify(..)字符串化。安全的JSON值是指能够呈现为有效JSON格式的值。

不安全的JSON值包括undefined、function、symbol和包含循环引用的对象。

如果要对含有非法JSON值的对象做字符串化,或者对象中的某些值无法被序列化时,就需要定义toJSON()方法来返回一个安全的JSON值。

如果对象中定义了toJSON()方法,JSON字符串化时会首先调用该方法,然后用它的返回值来进行序列化。

toJSON()返回的应该是一个适当的值(一个能够被字符串化的安全的JSON值),可以是任何类型,然后再由JSON.stringify(..)对其进行字符串化。

  1. var a = {
  2. b : 42,
  3. c : "42",
  4. d : [1,2,3]
  5. };
  6. console.log(JSON.stringify(a,["b","c"]));//"{"b":42,"c","42"}"
  7. console.log(JSON.stringify(a,function(k,v){if(k != "c"){return v;}}));//"{"b":42,"d",[1,2,3]}"

1.2 ToNumber

ES5规范定义了抽象操作ToNumber用于将非数字值转换为数字值。

其中true转换为1,false转换为0。undefined转换为NaNnull转换为0。

对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。

为了将值转换为相应的基本类型值,抽象操作ToPrimitive会首先检查该值是否有valueOf()方法,如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用toString()的返回值(如果存在)来进行强制类型转换。

  1. var a = {
  2. valueOf: function(){
  3. return "42";
  4. }
  5. };
  6. var b = {
  7. toString: function(){
  8. return "42";
  9. }
  10. };
  11. var c = [4,2];
  12. c.toString = function(){
  13. return this.join("");//"42"
  14. };
  15. Number(a);//42
  16. Number(b);//42
  17. Number(c);//42
  18. Number("");//0
  19. Number([]);//0
  20. Number(["abc"]);//NaN

1.3 ToBoolean

假值

  • undefined
  • null
  • false
  • +0、-0和NaN
  • ""

假值的布尔强制类型转换结果为false

所有的对象都是真值,假值对象除外。

假值对象不是包装了假值的封装对象。

  1. var a = new Boolean(false);
  2. var b = new Number(0);
  3. var c = new String("");
  4. var d = Boolean( a && b && c );
  5. console.log(d);//true
  6. //d为true,说明a、b、c都为true。

浏览器在某些特定情况下,在常规JavaScript语法基础上自己创建了一些外来(exotic)值,这些就是“假值对象”。

真值

真值就是假值列表之外的值。

  1. var a = "false";
  2. var b = "0";
  3. var c = "''";
  4. var d = Boolean( a && b && c );
  5. console.log(d);//true
  6. //所有字符串都是真值。
  1. var a = [];//空数组
  2. var b = {};//空对象
  3. var c = function(){};//空函数
  4. var d = Boolean( a && b && c );
  5. console.log(d);//true
  6. //[]、{}和function(){}都不在假值列表中,因此它们都是真值。

2.显式强制类型转换

我们在编码时应尽可能地将类型转换表达清楚,以免给别人留坑。类型转换越清晰,代码可读性越高,更容易理解。

2.1 字符串和数字之间的显式转换

  1. var a = 42;
  2. var b = String(a);//将数字转换为字符串
  3. var c = "3.14";
  4. var d = Number(c);//将字符串转换为数字
  5. console.log(b);//"42"
  6. console.log(d);//3.14
  7. var e = 5+ +c;//+c显式地将c转换为数字
  8. console.log(e);//8.14

日期显式转换为数字

  1. var d = new Date("Mon, 18 Aug 2014 08:53:06 CDT");
  2. console.log(+d);//1408369986000

我们不建议对日期类型使用类型转换,应该使用Date.now()来获得当前的时间戳,使用new Date(..).getTime()来获得指定时间的时间戳。

奇特的~运算符

  1. //~x大致等同于-(x+1)。
  2. ~42;//-(42+1) ==> -43
  3. //~和indexOf()一起可以将结果强制转换为真/假值。
  4. //JavaScript中字符串的indexOf(..)方法在字符串中搜索指定的子字符串,如果找到就返回子字符串所在的位置(从0开始),否则返回-1。
  5. var a = "Hello World";
  6. ~a.indexOf("lo");//-4 <-- 真值!
  7. if(~a.indexOf("lo")){//true
  8. //找到匹配!
  9. }
  10. //~-1的结果为0。
  11. ~a.indexOf("ol");//0 <-- 假值!
  12. !~a.indexOf("ol");//true
  13. if(!~a.indexOf("ol")){//true
  14. //没有找到匹配!
  15. }

字位截除

一些开发人员使用~~来截除数字值的小数部分,~~只适用于32位数字。

我们在使用~~~进行此类转换时需要确保其他人也能够看得懂。

2.2 显式解析数字字符串

  1. var a = "42";
  2. var b = "42px";
  3. Number(a);//42
  4. parseInt(a);//42
  5. Number(b);//NaN
  6. parseInt(b);//42

2.3 显式转换为布尔值

  1. var a = "0";
  2. var b = [];
  3. var c = {};
  4. var d = "";
  5. var e = 0;
  6. var f = null;
  7. var g;
  8. Boolean(a);//true
  9. Boolean(b);//true
  10. Boolean(c);//true
  11. Boolean(d);//false
  12. Boolean(e);//false
  13. Boolean(f);//false
  14. Boolean(g);//false
  15. !!a;//true
  16. !!b;//true
  17. !!c;//true
  18. !!d;//false
  19. !!e;//false
  20. !!f;//false
  21. !!g;//false
  22. //在if(..)..这样的布尔值上下文中,如果没有使用Boolean(..)和!!,就会自动隐式地进行ToBoolean转换。

3.隐式强制类型转换

隐式强制类型转换的作用是减少冗余,让代码更简洁。

3.1 字符串和数字之间的转换

  1. var a = "42";
  2. var b = "0";
  3. var c = 42;
  4. var d = 0;
  5. a + b;//"420"
  6. c + d;//42
  7. var e = [1,2];
  8. var f = [3,4];
  9. e + f;//"1,23,4"
  10. var g = 42;
  11. var h = g + "";
  12. h;//"42"
  13. var i = "3.14";
  14. var j = i - 0;
  15. j;//3.14

3.2 布尔值到数字的转换

  1. function onlyOne(){
  2. var sum = 0;
  3. for(var i = 0; i<arguments.length; i++){
  4. if(arguments[i]){
  5. //下面等同于 sum += Number(!!arguments[i]);
  6. sum += arguments[i];
  7. }
  8. }
  9. return sum == 1;
  10. }
  11. var a = true;
  12. var b = false;
  13. console.log(onlyOne(b,a));//true
  14. console.log(onlyOne(b,a,b,b,b));//true
  15. console.log(onlyOne(b,b));//false
  16. console.log(onlyOne(b,a,b,b,b,a));//false

3.3 隐式强制类型转换为布尔值

下面的情况会发生布尔值隐式强制类型转换:

  1. if(..)语句中的条件判断表达式。
  2. for(..;..;..)语句中的条件判断表达式。
  3. while(..)do..while(..)循环中的条件判断表达式。
  4. ? :中的条件判断表达式。
  5. 逻辑运算符||(逻辑或)与&&(逻辑与)左边的操作数。

3.4 || 和&&

和其他语言不同,在JavaScript中||&&的返回值是两个操作数中的一个(且仅一个)。即选择两个操作数中的一个,然后返回它的值。

  1. var a = 42;
  2. var b = "abc";
  3. var c = null;
  4. a || b;//42
  5. a && b;//"abc"
  6. c || b;//"abc"
  7. c && b;//null
  8. //a || b相当于a ? a : b
  9. //a && b相当于a ? b : a

对于||来说,如果条件判断结果为true就返回第一个操作数的值,如果为false就返回第二个操作数的值。

对于&&来说,如果条件判断结果为true就返回第二个操作数的值,如果为false就返回第一个操作数的值。



4.宽松相等和严格相等

宽松相等==检查值是否相等,严格相等===检查值和类型是否相等。

==允许在相等比较中进行强制类型转换,而===不允许。

4.1 抽象相等

字符串和数字之间的相等比较

  1. var a = 42;
  2. var b = "42"
  3. a === b;//false
  4. a == b;//true

其他类型和布尔类型之间的相等比较

  1. var a = "42";
  2. //不要这样用,条件判断不成立
  3. if(a == true){
  4. //..
  5. }
  6. //也不要这样用,条件判断不成立
  7. if(a === true){
  8. //...
  9. }
  10. //这样的显式用法没问题
  11. if(a){
  12. //..
  13. }
  14. //这样的显式用法更好
  15. if(!!a){
  16. //..
  17. }
  18. //这样的显式用法也很好
  19. if(Boolean(a)){
  20. //..
  21. }

null和undefined之间的相等比较

  1. var a = null;
  2. var b;
  3. a == b;//true
  4. a == null;//true
  5. b == null;//true
  6. a == false;//false
  7. b == false;//false
  8. a == "";//false
  9. b == "";//false
  10. a == 0;//false
  11. b == 0;//false

对象和非对象之间的相等比较

  1. var a = "abc";
  2. var b = Object(a);
  3. a === b;//false
  4. a == b;//true
  5. //a == b结果为true,因为b通过ToPromitive进行强制类型转换(也称为"拆封"),并返回标量基本类型值"abc",与a相等。
  1. var a = null;
  2. var b = Object(a);
  3. a == b;//false
  4. var c = undefined;
  5. var d = Object(c);
  6. c == d;
  7. var e = NaN;
  8. var f = Object(a);
  9. e == f;
  10. //因为没有对应的封装对象,所以null和undefined不能够被封装。
  11. //NaN能够被封装为数字封装对象,但拆封之后NaN == NaN返回false,因为NaN不等于NaN。

4.2 比较少见的情况

假值的相等比较

极端情况

安全运用隐式强制类型转换

  • 如果两边的值中有true或者false,千万不要使用==
  • 如果两边的值中有[]""或者0,尽量不要使用==

这时最好用===来避免不经意的强制类型转换。这两个原则可以让我们避开几乎所有强制类型转换的坑。

5.抽象关系比较

  1. var a = [42];
  2. var b = ["43"];
  3. a < b;//true
  4. b < a;//false
  5. var c = ["42"];
  6. var d = ["043"];
  7. c < d;//false
  8. var e = [4,2];
  9. var f = [0,4,3];
  10. e < f;//false
  11. //e转换为"4,2",f转换为"0,4,3",按字母顺序进行比较
  1. var a = {b:42};
  2. var b = {b:43};
  3. a < b;//false
  4. a == b;//false
  5. a > b;//false
  6. a <= b;//true
  7. a >= b;//true
  8. //根据规范a <= b被处理为b < a,然后将结果反转,因为b < a的结果是false,所以a <= b的结果是true。

参考资料:《你不知道的JavaScript》(中卷) 第四章

JS的强制类型转换的更多相关文章

  1. 《You dont know JS》强制类型转换

    强制类型转换 将值从一种类型转换为另一种类型通常称为类型转换,这是显式的情况.隐式的情况被称为强制类型转换 在书中,作者还提出一种区分方式: 类型转换发生在静态类型语言的编译阶段,强制类型转换发生在动 ...

  2. JS中强制类型转换

    JavaScript提供了3种强制类型转换的方法 一.Boolean()方法 该方法将指定的参数转换成布尔型.Boolean(object).参数object可以是字符串对象.数值对象.DOM对象等. ...

  3. JavaScript学习10 JS数据类型、强制类型转换和对象属性

    JavaScript学习10 JS数据类型.强制类型转换和对象属性 JavaScript数据类型 JavaScript中有五种原始数据类型:Undefined.Null.Boolean.Number以 ...

  4. Js里面的强制类型转换

    js 和 PHP语言一样是弱类型语言.近期我也在看C语言,并没有传说中那么难,既是书中一再强调的指针部分,也没有那么夸张.至少是理论和语法理解起来不是很难.看起来凡是什么东西,不要总是被别人的话迷惑了 ...

  5. JS 数据类型转换-转换函数、强制类型转换、利用js变量弱类型转换

    1. 转换函数: js提供了parseInt()和parseFloat()两个转换函数.前者把值转换成整数,后者把值转换成浮点数.只有对String类型调用这些方法,这两个函数才能正确运行:对其他类型 ...

  6. JS在if中的强制类型转换

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

  7. JS中的“==”与强制类型转换

    JavaScript中有“==”与“===”,那么他们有何区别呢? 对于基本数据类型, ===  (!==)只有当两个变量的类型和值都相等时,才返回true:而 == (!=)则会对变量进行强制类型转 ...

  8. Js中的假值_ES5中定义的ToBoolean方法强制类型转换后值为false

    你不知道的Javascript(中)--ToBoolean javascript中的值可以分为以下两类: 1.可以被强制类型转换为false的值 2.其他(被强制类型转换为true的值) 假值---以 ...

  9. js字符串转换为数字的三种方法。(转换函数)(强制类型转换)(利用js变量弱类型转换)

    js字符串转换为数字的三种方法.(转换函数)(强制类型转换)(利用js变量弱类型转换) 一.总结 js字符串转换为数字的三种方法(parseInt("1234blue"))(Num ...

随机推荐

  1. 解决Ubuntu“下载额外数据文件失败 ttf-mscorefonts-installer”的问题 (转载)

    解决Ubuntu“下载额外数据文件失败 ttf-mscorefonts-installer”的问题 发表于 2017-09-15 | 更新于 2018-04-29 | 分类于 Linux | 评论数: ...

  2. JavaWeb-Servlet-Tomcat

    Servlet就是运行在服务器上的Java类.Servlet容器为javaweb应用提供运行时环境,负责管理Servlet和JSP的生命周期,以及管理它们的共享数据. Servlet容器软件——Tom ...

  3. 关于go语言中的WaitGroup

    如果你刚接触Go语言并且想用它构建高并发,高性能的应用,弄明白WaitGroups是怎么回事很重要. 在本教程中,我们将掌握以下内容: WaitGroups的用途 一个WaitGroups的简单示例 ...

  4. 基于Neutron的Kubernetes SDN实践经验之谈

    首先,向大家科普下Kubernetes所选择的CNI网络接口,简单介绍下网络实现的背景. CNI即Container Network Interface,是一套容器网络的定义规范,包括方法规范.参数规 ...

  5. 字符串匹配:KMP算法, Boyer-Moore算法理解与总结

    1. KMP算法是前缀匹配算法,一次从前往后匹配的过程中,根据已经部分匹配的信息,在文本中,移动尽可能远的距离.而不是按照朴素模式匹配方法,每次都只移动一个位置. 比如这个示例,在文本串中从4(从0开 ...

  6. 个人作业Week7

    1.在做个人项目的时候,由于很久都没有写这么大的程序了,对程序的感觉还没有恢复,因此,没能完全完成个人项目.现在回去看个人项目的代码(针对完成的代码来看),完全就是一个大泥球,代码的结构性太差,基本上 ...

  7. 《Spring 2之站立会议3》

    <Spring 2之站立会议3> 昨天,查找了本机的端口号,并对代码作进一步的了解. 今天,对我们项目的基本框架进行了了解,即主界面和各个分界面的基本架构: 遇到的问题,虽然了解了基本框架 ...

  8. Scapy之ARP询问

    引言 校园网中,有同学遭受永恒之蓝攻击,但是被杀毒软件查下,并知道了攻击者的ip也是校园网.所以我想看一下,这个ip是PC,还是路由器. 在ip视角,路由器和pc没什么差别. 实现 首先是构造arp报 ...

  9. 【贪心算法】POJ-2376 区间问题

    一.题目 Description Farmer John is assigning some of his N (1 <= N <= 25,000) cows to do some cle ...

  10. 【图论】POJ-3255 次短路径

    一.题目 Description Bessie has moved to a small farm and sometimes enjoys returning to visit one of her ...