• 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 == " " //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('这条语句可以被执行');
    }

      

  • 相关阅读:
    http statusCode(状态码)
    MVC 获取控制器名称和Action名称(转载)
    Spark2.0机器学习系列之10: 聚类(高斯混合模型 GMM)
    机器学习算法(优化)之二:期望最大化(EM)算法
    Spark2.0机器学习系列之9: 聚类(k-means,Bisecting k-means,Streaming k-means)
    Spark2.0 特征提取、转换、选择之二:特征选择、文本处理,以中文自然语言处理(情感分类)为例
    Spark2.0 特征提取、转换、选择之一:数据规范化,String-Index、离散-连续特征相互转换
    SVM实现多分类的三种方案
    机器学习算法(优化)之一:梯度下降算法、随机梯度下降(应用于线性回归、Logistic回归等等)
    Spark2.0机器学习系列之8:多类分类问题(方法归总和分类结果评估)
  • 原文地址:https://www.cnblogs.com/solaZhan/p/12163033.html
Copyright © 2020-2023  润新知