zoukankan      html  css  js  c++  java
  • JavaScript 进阶(五)易混淆概念null vs undefined, == vs ===, string vs String

    先看一段代码

    [javascript] view plaincopy
     
    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,类型相同则同==。举例如下:
    [javascript] view plaincopy
     
    1. false == 0  
    2. true != 2  
    3. 1 == "1"  
    4. true != "true"  
    5. true == "1"  
    2. 对于对象(数组也是对象,String不是)
    == 与 === 没有区别,都是比较的它们的地址,不考虑它们的值,举例如下:
    [javascript] view plaincopy
     
    1. var a = {}  
    2. var b = a  
    3. a == b, a === b;  
    4. {} != {}  
    3. 如果是一方是对象,一方是基本数据类型呢?
    ===的时候,由于类型不同,则一定为false。==的情况下,先对对象调用toString函数,用toString的返回值与另一方的基本数据类型,用规则一进行比较。举例如下:
    [javascript] view plaincopy
     
    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
    事实上是这样的
    [javascript] view plaincopy
     
    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的处理,尽量集中地放在底层库,并且由资深程序员处理。
  • 相关阅读:
    Sum Root to Leaf Numbers
    Sum Root to Leaf Numbers
    Sort Colors
    Partition List
    Binary Tree Inorder Traversal
    Binary Tree Postorder Traversal
    Remove Duplicates from Sorted List II
    Remove Duplicates from Sorted List
    Search a 2D Matrix
    leetcode221
  • 原文地址:https://www.cnblogs.com/smght/p/4368389.html
Copyright © 2011-2022 走看看