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的存储在内存中的二进制表示。

[Effective JavaScript笔记]第3条:当心隐式的强制转换的更多相关文章

  1. [Effective JavaScript 笔记]第59条:避免过度的强制转换

    js是弱类型语言.许多标准的操作符和代码库会把输入参数强制转换为期望的类型而不是抛出错误.如果未提供额外的逻辑,使用内置操作符的程序会继承这样的强制转换行为. functin square(x){ r ...

  2. JS当心隐式的强制转换

    JavaScript对类型错误出奇的宽容 3 + true; // 4 null + 3; // 3 运算符+(加号)的重载 运算符+既重载了数字相加,又重载了字符串连接操作.具体是数字相加还是字符串 ...

  3. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  4. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  5. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  6. [Effective JavaScript 笔记]第54条:将undefined看做“没有值”

    undefined值很特殊,每当js无法提供具体的值时,就会产生undefined. undefined值场景 未赋值的变量的初始值即为undefined. var x; x;//undefined ...

  7. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  8. [Effective JavaScript 笔记]第37条:认识到this变量的隐式绑定问题

    CSVReader示例 需求 CSV(逗号分隔型取值)文件格式是一种表格数据的简单文本表示 张三,1982,北京,中国 小森,1982,东京,日本 吉姆,1958,纽约,美国 现需要编写一个简单的.可 ...

  9. [Effective JavaScript 笔记] 第2条:理解JavaScript的浮点数

    JavaScript数值型类型只有数字 js只有一种数值型数据类型,不管是整数还是浮点数,js都把归为数字. typeof 17;   // “number” typeof 98.6; // “num ...

随机推荐

  1. (旧)子数涵数·PS——冷色调与LOMO

    一.准备素材(我是从百度图库里下载的) 二.打开PS和素材 三.复制图层,快捷键Ctrl+J,并把原图层隐藏,只在副本上编辑(好习惯) 四.使用"匹配颜色"命令,增加"明 ...

  2. 在线富文本编辑器FckEditor配置(.Net Framework 3.5)

    进入FCKeditor文件夹,编辑 fckconfig.js 文件.1.上传设置  .  var _FileBrowserLanguage         = 'php' ;         // a ...

  3. 大型网站系统架构实践(六)深入探讨web应用集群Session保持

    原理 在第三,四篇文章中讲到了会话保持的问题,而且还遗留了一个问题,就是会话保持存在单点故障, 当时的方案是cookie插入后缀,即haproxy指负责分发请求,应用服务自行保持用户会话,如果应 用服 ...

  4. CSharpThinking---C#版本总结(附加三)

    C#版本总结: 日期 框架.net Visual Studio C# CLR 2002.2  1.0 2002 1.0 1.0 2003.4  1.1 2003 1.2 1.1 2005.11  2. ...

  5. 第三十课:JSDeferred详解1

    本课难度非常大,看一遍,蛋会疼,第二遍蛋不舒服,第三遍应该貌似懂了.初学者莫来,没必要,这完全就是一个研究. JSDeferred是日本高手cho45搞出来的,其易用性远胜于Mochikit Defe ...

  6. jQuery理解之(一)动画与特效

    本节主要降级和学习jQuery的自动显隐,渐入渐出.飞入飞出.自定义动画等. 1.显示和隐藏hide()和show() 对于动画来说,显示和隐藏是最基本的效果之一,本节简单介绍jQuery的显示和隐藏 ...

  7. ubuntu的命令day1

    ls -i    显示所有的文件,包括隐藏的文件. 以  .  开头的文件都是隐藏文件,可以在终端用ls -i显示所有的文件.比如.ssh linux生成密钥的命令如下: 1. cd .ssh/    ...

  8. [转]JVM 内存初学 (堆(heap)、栈(stack)和方法区(method) )

    这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解JAVA的底层和运行机制有比较大的帮助.废话不想讲了.入主题: 先了解具体的概念:JAVA的JVM的内存可分为3个区:堆(h ...

  9. POJ 2449Remmarguts' Date K短路模板 SPFA+A*

    K短路模板,A*+SPFA求K短路.A*中h的求法为在反图中做SPFA,求出到T点的最短路,极为估价函数h(这里不再是估价,而是准确值),然后跑A*,从S点开始(此时为最短路),然后把与S点能达到的点 ...

  10. .Net身份验证概述

    一直以来,所有的系统基本都会有用户的登陆验证过程,整个过程其实也不难理解,就是对于cookie的解析.微软的.Net平台围绕用户身份验证授权也有好几个版本了,从早期的Membership到Identi ...