js强制类型转换规则

这篇随笔记录一下js中数据的各种类型转换的规则,虽然很基础,但是重新过一遍会发现有些规范还是挺意想不到的

首先介绍一下ToString, ToNumber, ToBoolean 的转换规则

1、ToString

  规则1:null 转换为 “null” , undefined 转换为 “undefined” , true 转换为 “true” ;

  规则2:普通对象,除非自定义,否则用 toString();

      数组的toString()方法,先把所有单元字符串化,然后再用“,”连接;[1,2,3,4]  // “1,2,3,4”;

2、ToNumber

  规则1:布尔值 true 转换为 1, false 转换为 0; undefined 转换为 NaN; null 转换为 0;字符串处理失败时返回NaN;

  规则2:以 0开头地十六进制数会被当成十进制;

  规则3:对象会先被处理成相应地基本类型值,再按照值类型做相应处理;

      对象做ToPrimitive操作规则:先valueOf(), 如果valueOf的结果不是基本类型,再进行 toString();如果均不返回基本类型,则报TypeError;

      使用Object.create(null) 创建的对象,无valueOf 跟 toString 方法,故不能被强制类型转换

  规则4:空数组和空字符串,都会被转换为0:

Number("") // 0;
Number([]) // 0;

3、ToBoolean

  规则1:undefined,null,false,+0,-0,NaN,“” 会被转换为false;但是对应的封装对象为true

  规则2:js中 document.all  如果用在 if 语句中,会被转换为false;此条规则需要注意的是,在ie<= 10 的浏览器中,会返回true;

  规则3:除规则1和规则2,其他都返回true

Boolean(undefined) // false;
Boolean(null); // false;
Boolean(false); // false;
Boolean(+0); // false;
Boolean(-0); // false;
Boolean(""); // false;

const a = new Boolean( false) ;  // true
const b = new Number(0); // true
const c = new String("") ;  // true

if(document.all){
    // ie10 及 以下,会被打印; console.log('document.all 在if语句中被强制类型转换,但转换的结果为false,这条语句不会被打印'); }

  

JSON.stringify 的规则与 强制类型转换关联,故也总结到这里:

  JSON字符串化简单数值时,规则与toString大致相同;但是序列化的结果总是字符串:JSON.stringify("42")  //  ""42"";

  undefined、function、symbol 和包含对象间循环引用的,都属于不安全的JSON值,这些值会被忽略,数组种则会返回 null,仪保证单元位置不变

  例如:JSON.stringify([1,undefine,function(){},2])   // [1, null, null, 2];

     JSON.stringify( {a:1, b: undefine, c: function(){} } )  // {a:1};   此特性可用于发起请求时的无效参数过滤;

  可自定义toJSON 函数,在JSON序列化时会被调用;该函数应该返回一个能够被字符串化的安全的JSON值,而不是直接返回一个字符串,因为处理逻辑是对toJSON返回的字符串做字符化;

  JSON.stringify 支持传入可选参数replacer,用来指定对象序列化过程中属性的处理逻辑:

    a、当replacer为数组时, JSON.stringify 只序列化数组中包含的属性,例如:    

var a = {
      name:  "Lily",
      age:  12 ,
      hobby: ["basketball", "football"]        
}
JSON.stringify(a, ["name", "age"]);    //  "{"name": "Lily" , "age": 12}"

// 这个特性可以用来提取对象中指定属性

    b、当replacer为函数时, 函数会对将要被序列化的对象做一个遍历,入参为key 和 value,可在函数中写处理逻辑:

var a = {
      name:  "Lily",
      age:  12 ,
      hobby: ["basketball", "football"]        
}
JSON.stringify(a, function(key, value){    //   "{"name":"Lily","hobby":["basketball","football"]}"
    if(key !== "age") return value; 
});

    这里有一个细节: 当replacer函数第一次被调用时,是对对象本身的调用,key 为 undefined;

    属性的值为数组时,数组中的值也将被遍历,此时key为数组的索引,value为数组的单元值;

接下来,我们看下一些操作符在强制类型转换中的影响:

1、+  操作符

  +String : 会把字符串转换为数字;

  +Date:会把时间对象转换为时间戳;同样使用 Date.now(); new Date().getTime()也可以得到时间戳;

  String + other :首先 other 会被转换为字符串,再进行拼接操作;下面看一个例子:

 {} + []  //  0   此处 {} 出现在语句的开始,会被解析为代码块,[]被转换为数字0 

 [] + {}  // "[Object Object]"   此处遵循字符串相加规则,[] 为 “”, {}为 "[Object Object]"

  Number + Boolean:Boolean 会先被转换为Number类型,再进行相加操作

 -   x  /  等操作符,只针对Number类型有效,故这三个操作符会把操作数转换为数字类型;

2、位运算符 ~ 和 |   位运算符只适用于32位整数,故操作数会被ToInt32强制转换为32位格式;非数字类型的值会先通过ToNumber转换为 数字类型;

  ~ 运算(字位操作“非”)返回2的补码   ~x 大致等于  -(x+1);在~运算中,只有 x 为 -1 时,才会返回 0;这个特性可以用在将-1做为哨位值的运算中,避免暴露底层实现细节(抽象渗漏)

var a = "Hello world";
if(~a.indexOf("H")){
     // 找到匹配  
}

       | 运算符(字位操作“或”)

0 | -0    // 0
0 | NaN   // 0
0 | Infinity  // 0
0 | -Infinity  // 0

       !运算符 :非运算符常出现在布尔值的隐式转换中;

3、&& 和 || 运算 : 该逻辑运算(更形象地可以说是操作数选择器运算)会返回其中一个操作数:

  a && b  : 首先对第一个操作数做条件判断,结果为 true, 则返回 第二个操作数 b;如果为 false, 则返回第一个操作数a;

  a || b :  首先对第一个操作数做条件判断,结果为true,返回第一个操作数,条件判断结果为 false, 返回第二个操作数;

  这里有一个细节,a || b 的条件判断语句中,如果 a为true,则不会再继续执行 b;在这个特性下,可以做个小小的优化:

a ? a : b   可被优化为: a || b;

  另外一个优化点就是我们熟知的短路写法: b && b();   var a = b || 'default'; 等。

4、== 和 === 

  宽松相等(==)和严格相等(===)的区别在于,宽松相等允许在相等比较中进行强制类型转换。他们都会对值类型进行检查,区别在于类型不同时他们的处理方式。

  以下是 == 操作的一些规则:

  规则1:NaN 不等于 NaN;+0  等于 -0;

  规则2:对象的比较方式为:两个对象指向同一个值时,即为相等,不发生强制类型转换

  强制转换规则:

    a、字符串和数字之间:字符串先被转换成数字,再进行比较

    b、其他类型和布尔值之间:布尔值要先被转换为数字,再进行比较

    c、null 和 undefined之间的相等比较:null == undefined  // 返回true

    d、对象和非对象之间的比较:对象会被先转换为标量基本类型(字符串/数字/布尔值),然后再进行比较;

  下面看一些容易被迷惑的例子:

var a = null;
var b = Object(null);
a == b  // false;  null 没有对应的对象类型,故 d 相当于Object()创建的一个普通对象

var c = undefined;
var d = Object(d);
c == d  // false    undefined 没有对应的对象类型,故 d 相当于Object()创建的一个普通对象
var e = NaN; var f = Object(NaN); e == f // false f被包装为Number类型,拆封后为NaN,NaN == NaN // false

"0" == false // true 根据规则 b , 布尔值跟其他值比较,要先把布尔值转换为数字,false -> 0; "0" == 0 为true
false == "" // true 根据规则b, false 被转换为 0; 根据规则a,字符串 "" 也被转换为 0
false == [] // true [] 先调用 valueOf,返回数组本身,继续调用 toString, 返回空字符串 ""; false == "" 为 true
false == {} // false {} 先调用 valueOf,返回 {} ; 继续调用 toString, 返回字符串 "[object Object]" , false == "[object Object]" 为 false
[] == ![] // true 首先计算 ![] 根据布尔值的转换规则,[] 为 true , 那么 ![] 则为false 即 [] == false 结果为 true
2 == [2] // true [2] 调用 valueOf 返回数组对象[2], 继续调用toString, 返回 "2" , 2 == "2" 结果为 true
"" == [null] // true 同样valueOf 返回数组对象 [null], toString 返回 "" ;
0 == "\n" //true 空字符串,或者空格,都会被ToNumber转换为0
"true" == true // false 这个简单, 布尔值会先被转换为Number, "true" == 1 结果为 false

5、关于 <= 和 >=

  这次最让人意外的就是这俩操作符,我们来看个例子:

var a = {b: 42};
var b = {b: 43};
a < b  // false; 
a > b // false;
a == b // false;
a <= b  // true;
a >= b  // true;

 所以 <=  和 >=  为什么都为true? 

  首先,a 和 b都会先被处理成字符串 "[object Object]", 故  >  和 <  比较都为 false;根据对象的 == 运算规则,地址一致为相等, 故 == 比较也为false;

  根据 规范  a <= b 会被处理成 b < a, 然后将结果反转。因为 b < a 为 false, 故 a <= b 为 true;也就是说,js中的 <=  其实是 “不大于” 的意思。。。

另外,还有一些对数据类型做了转换的函数,也值得关注

1、parseInt() 显示解析数字字符串 

  和 Number的显示转换 不同,解析允许字符串中含有非数字字符,而Number显示转换则不允许:

Number("42px"); // NaN;   显示转换
parseInt("42px"); // 42   显示解析

Number(""); // 0
parseInt("") // NaN

  parseInt针对的是字符串值,如果向parseInt传入了一些奇奇怪怪的参数,那转换结果也是会出乎意料的:

  parseInt先将参数强制类型转换为字符串再进行解析

parseInt(0.000008)   // 0   首先被转换为字符串“0.000008”;然后被解析出 0;
parseInt(0.0000008)   // 8  首先被转换为字符串“8e-7”,  然后被解析出 8
parseInt(false,  16)   //  250   "false" 进行 16进制转换,前面的两个字母 “fa” 在16进制的字母内,被转换为250
parseInt(parseInt, 16)   // 15  首先被解析为字符串“function (){}...”  首字母 “f” 以十六进制转换规则,被转换为十进制 15  
parseInt("0x10")   //  16  0x开头为16进制,所以只看后两位0*16^0+1*16^1=16,所以0x10=16
parseInt("103", 2)   // 2  字符串103 以2进制规则转换,前两位 "10" 符合二进制数,被转换为 2, "3" 不在二进制表达范围内,故被舍弃,最终转换为十进制的结果为2; 

最后,提一下es6中符号类型,它的强制转换都必须是显式的,但转换为布尔值除外;并且符号不能够被强制类型转换为数字:

var s1 = Symbol("cool");
String(s1)  // "Symbol(cool)";
var s2 = Symbol("not cool");
s2 + "" ; // TypeError

Boolean(s1) ; // true
!!s1 // true
if(s1){
  console.log('这条语句可以被执行');
}

  

posted @ 2020-01-09 16:05  solaZhan  阅读(1579)  评论(0编辑  收藏  举报