先看一段代码

  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. BZOJ-1007-水平可见直线-HN2008

    描写叙述 在xoy直角坐标平面上有n条直线L1,L2,-Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆盖的. 比如,对于直线: L1:y=x; L2:y=- ...

  2. Python中NumPy基础使用

    Python发展至今,已经有越来越多的人使用python进行科学技术,NumPY是python中的一款高性能科学计算和数据分析的基础包. ndarray ndarray(以下简称数组)是numpy的数 ...

  3. eclipse+python+Anaconda ML环境搭建

    整个流程按照这个来: http://blog.csdn.net/olaking/article/details/42435451 你会发现pydev安装不了,听说服务器被和谐了,下面这篇文章告诉你如何 ...

  4. codeblock快捷键

    一款开源的C/C++ IDE(集成开发环境),基于wxWidgets GUI体系,跨平台支持. 从别处粘贴的,方便以后看,啦啦啦…… 编辑器 快捷键 功能 Ctrl+Z 恢复上一次操作 Ctrl+Sh ...

  5. xzzx

    创建包: CREATE OR REPLACE PACKAGE WYL_TEST_PKG_GGYW_XZZX IS -- Purpose : 公共业务-参保险种注销 --注销选择的险种,并将参保缴费信息 ...

  6. Qt 5.x 全局热键 for windows

    Qt 升级到5.x版本后,QAbstractEventDispatcher中函数发生变动,导致libqxt库中的qxtGlobalShortcut挂掉.参考qxtGlobalShortcut写了一个全 ...

  7. 深入浅出Win32多线程设计之MFC的多线程-线程与消息队列(经典)

    1.创建和终止线程 在MFC程序中创建一个线程,宜调用AfxBeginThread函数.该函数因参数不同而具有两种重载版本,分别对应工作者线程和用户接口(UI)线程. 工作者线程 CWinThread ...

  8. OGR SQL (GEOM)

    The OGRDataSource supports executing commands against a datasource via the OGRDataSource::ExecuteSQL ...

  9. csu1306: Manor

    http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1306 解题思路:唬人的水题,只要按照他的意思打,就能过,不过,数组最好开大点.用到优先队列,也可以 ...

  10. 10-IOSCore - 应用间通信、本地通知

    一.应用间通信 URL 调用系统服务: tel:11111 sms:xxx@163.com http:// URL深入 类型://主机:端口/地址?参数 label框等于文字大小快捷键:command ...