先看一段代码

  1. var foo = {}
  2. foo.a = null
  3. alert(foo.a == null)     //true
  4. alert(foo.a === undefined)  //false
  5. alert(foo.b == null)     //true
  6. alert(foo.b === null)   //false

直观上来看,foo.a 是 null, foo.b 是undefined,所以null == undefined 为true,null === undefined  为false。null与undefined似乎都代表空,== 与 === 都代表相等判断,那么JavaScript为什么要引入这两对相似的概念,它们又有什么区别呢?我们往下看。

null 与 undefined

null,代表空值,这个概念在绝大多数编程语言中都有,一般代表空的指针,比如在c和java中。一个生硬的解释就是“这个指针是存在的,但是指针是空值”,如果你尝试访问一个空指针指向的对象或地址,则会抛出“NPE(NullPointerException)”。而undefined似乎就是JavaScript的特产了,至少C和Java都没有这个概念。undefined顾名思义,就是未定义,那么为C/Java没有呢?事实上它们俩也是有的,但是undefined在编译的时候就已经被处理掉了。“变量未定义”这个编译错误我相信写过C/Java的人都遇到过吧。有了上面的解释,那我们来重新理解一下null和undefined,null指的是“变量存在,其值为空”,undefined则代表”该变量不存在,或未初始化“。此外,它们的类型也是不同的,typeof(null) == "object", typeof(undefined) == "undefined"
在参与数字运算时,null则被当成0处理,undefined参与任何数字运算返回值均为NaN(Not a Number. 但typeof(NaN) == "number", 擦,这不是自抽耳光么,WTF)。null 与 undefined与字符串相加时,分别被转换成“null”, "undefined"

== 与 ===

这两种等于号对于不同的数据类型要分开理解
1. 对于string,数字,布尔。
== 时候,如果类型相同,那么用它们的值进行比较,判断true/false,如果类型不同,则先转换成相同类型,在比较值。转换类型的规则是:布尔转成数字(true->1, false->0),数字转成String,或是布尔转成数字,再转成string(true->"1", false->"0"),转成类型相同之后,再比较值。对于===,如果类型不同,直接返回false,类型相同则同==。举例如下:
  1. false == 0
  2. true != 2
  3. 1 == "1"
  4. true != "true"
  5. true == "1"

2. 对于对象(数组也是对象,String不是)

== 与 === 没有区别,都是比较的它们的地址,不考虑它们的值,举例如下:
  1. var a = {}
  2. var b = a
  3. a == b, a === b;
  4. {} != {}

3. 如果是一方是对象,一方是基本数据类型呢?

===的时候,由于类型不同,则一定为false。==的情况下,先对对象调用toString函数,用toString的返回值与另一方的基本数据类型,用规则一进行比较。举例如下:
  1. "abc" !== new String("abc")
  2. "abc" == new String("abc")
  3. 1 == new String("1") // 坑爹的string和String,下文会详细的讲“字符串”
  4. var a = {}
  5. a == "[object Object]"
  6. a.toString = function(){return "1";}
  7. a == 1

坑爹的String和string

为什么我说String很坑爹呢?我发觉直到我写这篇文章之前,我对JavaScript的String的理解都是错误的。我之前都把string理解为对象,一个不可变的对象。比如我的几周之前的上一篇博文还是这么理解的http://blog.csdn.net/qq_21930351/article/details/40627649
事实上是这样的

  1. var a = "abc";
  2. typeof(a) == "string"
  3. var b = new String("abc")
  4. typeof(b) == "object"
  5. a == b //参考上一段的规则3,b.toString() == "abc"
  6. //之前一直认为a, b 都是对象, 并且类型相同,都是那个“string”,直到我今天为了写博文,测试了如下代码:
  7. var s1 = new String("abc");
  8. var s2 = new String("abc");
  9. s1 != s2 //what a fuck
  10. //其中==是比较两个string的内容,事实上 s1 != s2,所以我就纳闷了。事实上他们俩都是object,适用于上一篇的规则2.

所以代码中直接用双引号产生的string和new String出来的东西还是有点区别的。

尽管如此,它们在绝大多数情况是可以混用的,除了typeof。String类型的toString函数就是返回一个字符串的常量,所以可以直接用==来比较内容。此外,它们俩还共享prototype,你扩展了String的prototype之后,string的prototype也会跟着改。
所以到底是String还是string,具体问题具体看吧。

有什么用?

这个有什么用?一个知识说出来,总有人会问这个问题。你可能会说,你这个纯学院派的,冷门知识扣那么细,有啥用啊,还不是孔乙己“‘茴’有四种写法”。我给的答案是,大部分情况,的确没什么用。比如那个坑爹的string,我写了那么久的JS,一直都是错误理解,还不是照写不误?然而看到“有什么用?”这个问题的时候,我总是会想起乔布斯的演讲的片段:

我没预期过学这些东西能在我生活中起些什么实际作用,不过十年后,当我在设计第一台麦金塔(Macintosh)时,我想起了当时所学的东西,所以把这些东西都设计进了Mac里,那是第一台使用了漂亮印刷字体的电脑。如果我当时没有退学,就不会有机会去参加这个我感兴趣的美术字课程,Mac就不会有这么多丰富的字体,以及赏心悦目的字体间距。因为Windows照抄了Mac,所以现在个人电脑才能有这么美妙的字型。

上面是鸡汤啊,看起来没啥料,下面加点私货,说说我对编程中的一些所谓“冷门知识”的观点:

1. 大部分情况它们就是“茴”字的四种写法,确实没用,因为这些生僻的语言元素很少用到,并且即使用到了,也往往有不冷门的替代方案。
2. 然而语言的设计者都是计算机界的佼佼者,他们不会无缘无故的放一些没用的元素在里面的,你敢说你比这些语言的设计者更牛?既然放进去,就一定有其道理。
3. 如果觉得它们没用,那么一般就是功力或经验还没达到一定的高度的缘故。
4. 下面两种场景下,这些“冷门”的知识就很好用了,一是某个匪夷所思的bug就是因为对语言理解不深导致。二是当开发一个大型项目的底层库和框架的时候,用好这些知识能够事半功倍。
 
上面的内容仍然是大道理啊,还是没能解释一下这篇文章看了对JavaScript的实战有何帮助。下面有点干货,不多,充分说明这些元素真的很少被使用,或者是我的火候还不够啊。
1. 函数重载。 比如一个库函数foo(a, b). 当调用方调用foo(a)的时候,foo函数内部的b就是undefined。在foo函数内部,检查参数是否===undefined来确定调用方到底是foo(a) 还是foo(a, null).
2. 断言。开发阶段使用断言能够让代码在出错点立刻崩溃,防止错误蔓延,从而提高调试效率。在使用断言时,要确保值和类型都正确无误,所以要使用===。
3.  到底是返回null还是undefined? 对于数值计算,如果计算参数不合适,要么抛出异常,要么返回null或0作为空值。避免返回undefined,这样做会导致它的上层算式变成NaN,可能会导致错误蔓延。对于需要返回string的地方,如果出错,则要么抛出异常,要么返回空串,避免返回null或是undefined,否则就会出现“null”, "undefined"的字符串。如果应该返回对象,则应该返回null,而避免返回undefined。null和对象类型相同,防止在需要判断变量类型的地方由于一个“undefined”导致控制逻辑跑到不该去的地方。然而对于一个没有返回值的过程,则应该明确的返回undefined或是直接写return或者干脆不写return语句。
4. 如果你需要判断一个对象是否拥有某一个属性,则应该使用hasOwnProperty函数。这个函数无论该属性是null还是undefined,只要属性存在,则都返回true。此外,如果你需要明确的删除一个属性,则使用delete,而不要仅仅将其置null,undefined。null,undefined不仅仅会被hasOwnProperty返回true,同时这个属性仍然是可枚举的,就是用 for - in 来遍历对象的时候仍然能够被遍历出来。
5. 修改String的prototype。比如我们的代码中经常需要反转一个字符串,那么每次都这样调用reverse(s1)。这样则需要在全局名空间定义一个reverse函数。然后可以通过修改prototype来给所有的String对象提供该函数。String.prototype.reverse() = function(){}. 当这么做以后,无论是new String还是 “”字符串常量,均可以使用"abc".reverse()来返回一个反转后的字符串。
6. 编写代码的时候尽量依赖内容判断而非类型。因为 == 和 === 都判断相等,。null 和 undefined都代表”无“,在阅读代码的时候很容易就直接在逻辑层面就认为它们等价了。在一段逻辑中,如果是处理数字,那么推荐在逻辑入口处将所有的入参转为数字,那么逻辑内部就不会因为== 和 ===问题出错,字符串也类似。在判断值是否为空时,尽量让分支逻辑不依赖null还是undefined。如果你的代码中typeof比较多而散布的时候,就要重新考虑一下设计的问题了。让业务逻辑层面关注内容,至于typeof,==/===, null/undefined的处理,尽量集中地放在底层库,并且由资深程序员处理。

JavaScript 进阶(五)易混淆概念null vs undefined, == vs ===, string vs String的更多相关文章

  1. 【C#小知识】C#中一些易混淆概念总结(五)---------继承 分类: C# 2014-02-06 22:05 1106人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  2. 【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  3. C#中一些易混淆概念总结

    C#中一些易混淆概念 这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框 ...

  4. 【C#小知识】C#中一些易混淆概念总结(七)---------解析抽象类,抽象方法

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  5. 【C#小知识】C#中一些易混淆概念总结(六)---------解析里氏替换原则,虚方法 分类: C# 2014-02-08 01:53 1826人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...

  6. 【C#小知识】C#中一些易混淆概念总结(二)--------构造函数,this关键字,部分类,枚举 分类: C# 2014-02-03 01:24 1576人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 继上篇对一些C#概念问题进行细节的剖析以后,收获颇多.以前,读书的时候,一句话一掠而 ...

  7. 【C#小知识】C#中一些易混淆概念总结(四)---------解析Console.WriteLine() 分类: C# 2014-02-05 17:18 1060人阅读 评论(0) 收藏

    目录: [C#小知识]C#中一些易混淆概念总结 [C#小知识]C#中一些易混淆概念总结(二) [C#小知识]C#中一些易混淆概念总结(三) ------------------------------ ...

  8. 【C#小知识】C#中一些易混淆概念总结(三)---------结构,GC,静态成员,静态类

    目录: [C#小知识]C#中一些易混淆概念总结 [C#小知识]C#中一些易混淆概念总结(二) ---------------------------------------分割线----------- ...

  9. 【C#小知识】C#中一些易混淆概念总结---------数据类型存储,方法调用,out和ref参数的使用

    这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框架类库) 这些类是微软 ...

随机推荐

  1. JQ 一些基本方法

    1.判断复选框是否有选中,bischecked 返回 ture 或 false var bischecked = $('[name=uid]').is(':checked'); 2.查看当前元素是父元 ...

  2. 小猪猪逆袭成博士之C++基础篇(二) 常量、处理类型、自定义头文件

    小猪猪逆袭成博士之C++基础篇(二) const .auto. decltype 上一章我们介绍了一些常用的类型和常见的问题,下面再介绍一些学习的时候不是特别常用但是在实际工程中很有用的一些东西. 一 ...

  3. BZOJ 3626: [LNOI2014]LCA( 树链剖分 + 离线 )

    说多了都是泪啊...调了这么久.. 离线可以搞 , 树链剖分就OK了... -------------------------------------------------------------- ...

  4. 【集训笔记】二分图及其应用【HDOJ1068【HDOJ1150【HDOJ1151

    匈牙利算法样例程序 格式说明 输入格式: 第1行3个整数,V1,V2的节点数目n1,n2,G的边数m 第2-m+1行,每行两个整数t1,t2,代表V1中编号为t1的点和V2中编号为t2的点之间有边相连 ...

  5. 一致性算法--Paxos

    分布式一致性算法--Paxos Paxos算法是莱斯利·兰伯特(Leslie Lamport)1990年提出的一种基于消息传递的一致性算法.Paxos算法解决的问题是一个分布式系统如何就某个值(决议) ...

  6. hpu第五届acm比赛

    vijos P1211生日日数   描述 CCC老师的生日是YY年MM月DD日,他想知道自己出生后第一万天纪念日的日期(出生日算第0天). 格式 输入格式 从文件的第一行分别读入YY,MM,DD其中1 ...

  7. Dockerfile指令总结

    指令的一般格式为INSTRUCTION arguments,指令包含FROM.MAINTAINER.RUN等. FROM 格式为FROM <image>或FROM <image> ...

  8. 使用gettimeofday测试函数运行的时间

    #include <time.h> #include <stdio.h> #include<sys/time.h> #define NEW_TIME_VALE st ...

  9. 【转】linux命令useradd添加用户详解

    在linux中增加用户我们使用useradd命令而删除用户直接使用userdel即可了,下面小编来给各位同学介绍一下在linux中添加与删除用户方法吧.   1.作用 useradd或adduser命 ...

  10. 浅谈MySql的存储引擎(表类型) (转)

    什么是MySql数据库 通常意义上,数据库也就是数据的集合,具体到计算机上数据库可以是存储器上一些文件的集合或者一些内存数据的集合. 我们通常说的MySql数据库,sql server数据库等等其实是 ...