先看一段代码

  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. veridata实验例(5)在更改主键列值,update操作将被分成两个语句

    veridata实验例(5)更改主键列值,update操作将被分成两个语句 续接"veridata实验举例(4)验证veridata查找出updata.delete操作导致的不同步现象&qu ...

  2. Swift主题色顶级解决方案一

    一.常规主题色使用点 应用在发布前都会对其主题色进行设置,以统一应用的风格(可能有多套主题).在主题色设置上有几个方面,如下: 1.TabBar部分,设置图片高亮.文本高度颜色 2.Navigatio ...

  3. stm32基础入门

    1.开发工具,初学者建议MDK,后期ivr 2.寄存器开发or库 版本开发:先寄存器开发,后期两者结合: 3.软件仿真or开发板,先软件仿真,后期两者结合: 建立工程: 1.包含三部分:start.u ...

  4. c++,纯虚函数与抽象类

    1.纯虚函数的定义: (1)虚函数被“初始化”为0的函数.声明纯虚函数的一般形式是virtual 函数类型 函数名(参数表列) =0;(2)纯虚函数没有函数体:(3)最后面的“=0”并不表示函数返回值 ...

  5. DOM操作HTML文档

    概述 之前写过一些关于DOM方法的知识,理论方法的偏多,所以,这篇博客主要是实践方面的Demo,如果,大家觉得理论方面有所欠缺,大家可以看看这几篇博客:JavaScript总结(一.基本概念)和Jav ...

  6. [javascript]在浏览器端应用cookie记住用户名

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. {key}面向对象程序设计-C++ polymorphism 【第十三次上课笔记】

    Peronal Link: http://segmentfault.com/a/1190000002464822 这节课讲了本门课程 面向对象程序设计中最为重要的一个部分 - 多态 /******** ...

  8. python下载文件(图片)源码,包含爬网内容(爬url),可保存cookie

    #coding=utf-8 ''' Created on 2013-7-17 @author: zinan.zhang ''' import re import time import httplib ...

  9. android的fragment基本介绍

    可以分为下面的几部分: 使用支持库 创建一个Fragment 创建一个动态UI 多个Fragment之间的通信 1.使用支持库 如果您的应用需要运行在3.0及以上的版本,可以忽略这部分内容. 如果您的 ...

  10. BZOJ 2212: [Poi2011]Tree Rotations( 线段树 )

    线段树的合并..对于一个点x, 我们只需考虑是否需要交换左右儿子, 递归处理左右儿子. #include<bits/stdc++.h> using namespace std; #defi ...