zoukankan      html  css  js  c++  java
  • [Effective JavaScript笔记]第3条:当心隐式的强制转换

    js对类型错误出奇的宽容

    3+true;  //4

    3*””;  //0

    3+[]; //3

    3+[3]; //33

    以上表达式在许多语言早就变红了。而js不但不报错还给你个结果。

    极少情况会产生即时错误,非函数对象或试图选择null的属性。

    “hello”(1);//error:not a function

    null.x; //error:cannot read property ‘x’of null

    大多数情况是不会抛错的,按照多种多样的方式进行着转换。

    运算操作

    算术运算符在计算之前会尝试将参数转换为数字。

    “17”* 3 =>51

    18 / “3” =>6

    “10”-“ 4”=>6

    ”+  ”号比较特殊除了数字还能连接字符串。取决于其参数的类型。

    2+3; //5

    “hello”+”world”; //”hello world”

    “2”+ 3  ; // “23”

    2+”3”;//23

    合并数字和字符串时,js偏爱字符串。所以上面的

    “2”+(3).toString();//”23”

    (2).toString()+”3”;//”23”

    1+2+”3”;//”33”

    1+”2”+3;//”123”

    为什么呢。

    加法运算是自左向右的,(也就是看到了才算,+是两元操作符,只看到左边和右边。)

    1+2+”3”=> (1+2)+”3”=>3+”3”=>”33”

    1+”2”+3=>(1+”2”)+3=>”12”+3=>”123”

    位运算符(~,&,^和|)以及移位运算符(<<,>>和>>>)不仅会将操作数转换为数字,而且还会将操作数转换为32位整数。

    “8”|“1”=>9

    “8”& true => 0

      ~“8” => –9

    8^2 => 10

    8<<1=>16 相当于8*2

    8>>1=>4 相当于8/2

    -8>>>1=>2147483644

    8>>>1=>4

    (>>>和>>区别在于,符号位动不动,>>>是连符号位都向右移动,所以负数会变很大,正数值不变,负数的符号位为1,正数的符号位为0。(具体的查书去))

    NaN

    强制转换也会隐藏错误。结果为null的变量在算术中不会导致错误因为null会转化为0,一个未定义的变量将被转换为特殊的浮点数NaN。在测试NaN时也会遇到问题因为NaN不等于本身。测试一个值是否等于NaN行不通。

    var x=NaN;

    x===NaN; //false

    isNaN也不可靠,也会带有隐式转换。

    isNaN(“2”); //false

    isNaN(“2s”); //false

    isNaN(null); //false

    isNaN([]); //false

    对于绝对不是NaN,但会被转化为NaN的值,使用isNaN是无法区分的。

    isNaN(“s2”); //true

    isNaN(undefined); //true

    isNaN({}); //true

    isNaN({valueOf:”s2”}); //true

    下面还有两个要注意的值

    isNaN(NaN);//false

    isNaN(Infinity); //false

    NaN和Infinity会先转化为0

    NaN是唯一一个不和自身相等的值,可以通过这个写出下面这个函数

    function isReallyNaN(x){

        return x!==x;

    }

    调试

    强制转换使得调试很难,因为没法确定一些类型错误。当计算出问题,最好的调试是在检查计算的过程中,回到出错的“最后一点”。检查每个操作参数,查看错误类型的参数。根据不同的错误,可能是逻辑错误,也可能是类型错误。

    对象

    对象也会被转换为原始值。最常见的是转换为字符串。

    '我的类型是:'+Math;//"我的类型是:[object Math]"

    对象通过隐式调用自身的toString方法进行转换。可以直接调用一下试试。

    Math.toString(); //"[object Math]"

    对象也可以通过valueOf()方法转换为数字

    2*{valueOf:function(){return 100;}};//200

    2+{valueOf:function(){return 100;}};//102

    "2"+{toString:function(){return "000"}};//”2000”

    "2"+{toString:function(){return "000"},valueOf:function(){return 1000}}; //”21000”

    上面这个运算没法说明+号是在做字符串连接还是加法根据参数的类型,因为存在隐式类型转换,因此类型并不明显,js中优先选择valueOf方法而不是toString方法来解决含糊的问题

    (如果字符串连接应该调用toString方法,但上面并没有。而是调用了valueOf方法,所以这里很含糊)

    valueOf是专门为那些代表数值的对象设计的。对于数值对象toString和valueOf方法应该返回相同的值,只是不同的类型。不管是对象的连接和相加+运算的结果都是相同的。最好避免使用valueOf方法,除非对象真的是数字的抽象,并且保证toString方法产生一个valueOf方法结果的字符串表示。

    真值运算

    if,||和&&等逻辑运算上需要布尔值作为参数,但实际上可以接受任何值。js按照简单的隐式转换规则将其它值解释为布尔值。大多数js的值都为真,隐式都可转化为true。对于字符串与数字以外的对象,真值运算不会隐式调用任何强制转换方法。js中有7个假值:false,0,-0,””,NaN,null和undefined。其他所有值都为真值。

    使用真值去检查函数参数或者对象属性是否已定义不是绝对安全的。

    如:

    function point(x,y){
        if(!x){
          x=320;
        }
        if(!y){
           y=240;
         }
         return {x:x,y:y}
    }

    此函数忽略任何为假值的参数,包括0

    point(0,0); //{x:320,y:240}

    检查参数是否为undefined更为严格,可以使用typeof

    function point(x,y){
        if(typeof x === ‘undefined’){
           x=320;
        }
        if(typeof y === ‘undefined’){
           y=240;
         }
         return {x:x,y:y}
    }

    这种方式可以正确识别0和undefined

    point(); //{x:320,y:240}

    point(0,0); //{x:0,y:0}

    另一种方式和值undefined比较

    if(x===undefined){….}

    效果和第二种方式相同

    总结

    1、类型错误会被隐式的强制转换隐藏

    2、运算符+是进行加法运算还是字符串连接操作取决于参数类型。

    3、valueOf强制转换为数字,toString强制转换为字符串

    4、实现valueOf方法的对象,应该实现一个toString方法返回valueOf方法返回值的字符串表示

    5、测试一个值是否未定义,应该使用typeof或者直接与undefined比较,不应该使用真值运算

    附录:补码的计算方法(高3中拿来的)

    负值是以二进制补码的方式存储。计算一个数值的二进制补码,经过3个步骤(以-18为例)

    (1)求这个数值的绝对值的二进制码

    18的二进制码

    0000 0000 0000 0000 0000 0000 0001 0010

    (2)求二进制反码,即将0替换为1,将1替换为0

    18的二进制反码

    1111 1111 1111 1111 1111 1111 1110 1101

    (3)得到的二进制反码加1

    1111 1111 1111 1111 1111 1111 1110 1101

                                                                         1

    --------------------------------------------------

    1111 1111 1111 1111 1111 1111 1110 1110

    这个是-18的存储在内存中的二进制表示。

  • 相关阅读:
    MySQL 通过多个示例学习索引
    git reset的用法
    git rebase的用法
    学习yii2.0——依赖注入
    学习yii2.0——行为
    学习yii2.0——事件
    学习yii2.0——数据验证
    让Apache和Nginx支持php-fpm模块
    安装python3
    使用php操作memcache
  • 原文地址:https://www.cnblogs.com/wengxuesong/p/5463026.html
Copyright © 2011-2022 走看看