8

最近在写公司的登录注册模块,遇到类型不同相比较的时候,就心惊胆战,每次都要用浏览器来验证一下,决定乱七八糟的随便写一下,方便日后自己回顾知识~

弱类型带来的那些让人迷糊的事

弱类型带来的好处之一:终于不用自己确定数据类型了。

写C++这类的强类型语言,每次都要先确定是intchar还是string之类的,但是仅仅通过var,怎么解决工作过程中的不同数据类型的比较问题的呢?

这个问题烦了我很久,当初学js的时候就只能记住最简单的其他类型到boolean的转换,涉及到其他的,只能晕菜。。。

下面分几步来简单的探探不同类型的转换吧~

以下的内容,都可以从《JavaScript权威指南》中找到。

其他值转换成boolean

在编写JavaScript代码的过程中,几乎不用考虑它的取值类型。在工作过程中,我们有可能会需要在if的条件判断中对参数进行判断,这就涉及到了boolean值的转换。

当JavaScript期望使用一个布尔值的时候,你可以提供任意类型值,JavaScript将根据需要自行转换类型,一些值(真值)转为true,其他值(假值)转为false--《JavaScript权威指南》(page 48)

对于布尔值类型的转换,实际上只需要特别记住几个特殊的会转换成false的值就可以了

这些值包括:undefinednullfalse""0-0NaN

所有的其他值(包括所有对象)都会转换成true

一般在工作中,我们可以直接使用:

if (value){
    ...
}

根据js的语言特性,可以让浏览器直接替我们判断,省时省力,我个人不太喜欢下面的写法:

if (value == null){
    ...
}

尽管这样也可以达成目标,但是总感觉多敲了代码,还需要特别注意如果不是null或者undefined,但也是假值的情况。

需要特别注意:假值中包含0 or -0的情况

还有一个小点需要特别注意一下:jQuery中选择符选择某个DOM节点,也许这个DOM节点并不存在,但是$(selector)返回的值是一个对象,所以,我们判断是否存在这个DOM,需要通过$(selector).length > 0来判断是否存在。

原始值转换成字符串

原始值:数字字符串布尔值nullundefined

原始值转换成字符串的情况估计是最简单的了,可以直接理解成在原始值的两边加上""就行了

undefined --> "undefined"
null      --> "null"
true      --> "true"
false     --> "false"
0         --> "0"
-0        --> "-0"
NaN       --> "NaN"
Infinity  --> "Infinity"
-Infinity --> "-Infinity"
1         --> "1"
......

原始值转换成数字

除了字符串,大部分原始值都会有固定的转换结果,如下:

undefined  --> NaN
null       --> 0
true       --> 1
false      --> 0

但是,字符串的转换有点微妙,给我的感觉有点类似Number()方法,但是注意,只是类似,调用Number()会生成包装对象,而不是一个数字。

字符串转换成数字,会有以下的特点:

  1. 以数字表示的字符串可以直接转换成数字

  2. 允许字符串开头跟结尾带有空格

  3. 在开始和结尾处的任意非空格符都不会被当成数字直接量的一部分,转换结果变成NaN

结果如下:

"one"  --> NaN
"u123" --> NaN
" 123" --> 123
"123 " --> 123
" 12 " --> 12

原始值转换成对象

原理:原始值通过调用String()Number()Boolean()构造函数,转换成他们各自的包装对象

存取字符串、数字或者布尔值的属性的时候创建的临时对象叫做包装对象,他只是偶尔用来区分字符串值和字符串对象、数字和数值对象以及布尔值布尔对象。通常,包装对象只是被看做是一种实现细节,而不用特别关注。--《JavaScript权威指南》(page46)

但是nullundefined信仰比较坚定,就是不想转成对象,从而,当将它们用在期望是一个对象的地方都会抛出一个错误throws TypeError

注意:只是在转换的过程中会抛出Error,在显性创建对象的时候,不会报错
new Object

对象转换成原始值

对象转换成原始值,可以分成以下三种:

  1. 对象转换成boolean

  2. 对象转换成字符串(object-to-string)

  3. 对象转换成数字(object-to-number)

其中,对象转换成boolean,规则非常简单:所有的对象都转换成true(包括包装对象)

注意new Boolean(false)是一个对象,转换成布尔值也是true

而对象转换成字符串以及对象转换成数字根据场景不同,有些复杂,在学习了解他们的转换过程之前,先来看两个函数:toString()以及valueOf()

1、在MDN中搜索toString,你会发现如下情形:
toString

如果细细查看,你会发现:所有的对象都有toString()方法,作用就是返回一个反映这个对象的字符串。

但是,这个方法返回的值有些时候不是那么的好。

例如:Object.prototypr.toString()

默认情况下,每个对象都会从Object上继承到toString()方法,如果这个方法没有被这个对象自身或者更接近的上层原型上的同名方法覆盖(遮蔽),则调用该对象的toString()方法时会返回"[object type]",这里的字符串type表示了一个对象类型。

例如:

var o = new Object();
o.toString()    //返回[object Object]

很多类定义了很多特定版本的toString()方法:

  1. 数组类Array.prototype.toString()

  2. 函数类Function.prototype.toString()

  3. 日期类Date.prototype.toString()

  4. 正则类RegExp.prototype.toString()
    ······
    使用方法及返回结果如下:
    objecttoString

2、在MDN搜索valueOf,可以得到与toString相似的结果。

但是,这个方法的任务并没有详细的定义,我甚至无法从MDN的描述中得出它与toString的区别。

JavaScript calls the valueOf method to convert an object to a primitive value. You rarely need to invoke the valueOf method yourself; JavaScript automatically invokes it when encountering an object where a primitive value is expected.

也许从下面的代码中我们可以初窥一二:

[1,2,3].toString() // --> "1,2,3"
[1,2,3].valueOf() // --> [1, 2, 3]
(function(x){f(x);}).toString() // --> "function (x){f(x);}"
(function(x){f(x);}).valueOf() // --> function anonymous(x)
/\d+/g.toString() // -->"/\d+/g"
/\d+/g.valueOf() // --> /\d+/g
new Date(2010,0,1).toString() // --> "Fri Jan 01 2010 00:00:00 GMT+0800 (中国标准时间)"
new Date(2010,0,1).valueOf() // --> 1262275200000

根据上面的结果,可以尝试得出以下的结论:


对象是复合值,而且大多数对象无法真正表示为一个原始值,因此默认的`valueOf()`方法简单地返回对象本身,而不是返回一个原始值

数组、函数以及正则表达式简单地继承了这个默认的方法,而日期类定义的valueOf()方法返回它的一个内部表示:1970年1月1日以来的毫秒数

有了上面的两个方法,我们就可以进行对象到字符串以及对象到数字的转换了。

对象到字符串的转换的步骤如下:

  1. 如果对象具有toString()方法,那么就调用这个方法,返回一个原始值,如果这个值本身不是字符串,JavaScript会将其转换成字符串,并返回字符串的结果。

  2. 如果没有toString()方法,或者这个方法不返回一个原始值,那么调用valueOf()方法,返回一个原始值,如果这个值本身不是字符串,JavaScript会将其转换成字符串,并返回字符串的结果。

  3. 否则JavaScript无法从toString()或者valueOf()获得一个原始值,这时抛出一个类型错误。

对象到数字的转换的步骤如下:

    1. 如果对象具有valueOf()方法,那么就调用这个方法,返回一个原始值,如果这个值本身不是字符串,JavaScript会将其转换成字符串,并返回字符串的结果。

  1. 如果没有valueOf()方法,或者这个方法不返回一个原始值,那么调用toString()方法,返回一个原始值,如果这个值本身不是字符串,JavaScript会将其转换成字符串,并返回字符串的结果。

  2. 否则JavaScript无法从toString()或者valueOf()获得一个原始值,这时抛出一个类型错误。

结束了?你还没考虑应用呢!

由于JavaScript是弱类型语言,所以我们对于类型转换的应用其实有许多。

例如:if判断、+运算符以及==运算符等等

关于+

  1. 进行数字加法以及字符串连接操作

  2. 如果其中的一个操作数是对象,则将对象转换成原始值

==也是js判断中常用的涉及到类型转换的一个运算符
关于==

  1. 如果两个值类型相同,进行===比较

  2. 如果两个值类型不同,他们可能相等,需要按照一定的规则来进行类型转换并比较

==类型不同时的转换规则:

  1. 如果一个值是null,另一个值是undefined,则相等

  2. 如果一个是字符串,另一个值是数字,则把字符串转换成数字,进行比较

  3. 如果任意值是true,则把true转换成1再进行比较;如果任意值是false,则把false转换成0再进行比较

  4. 如果一个是对象,另一个是数值字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的toString或者valueOf方法。 js核心内置类,会尝试valueOf先于toString可以理解为对象优先转换成数字);例外的是DateDate利用的是toString转换。非js核心的对象,通过自己的实现中定义的方法转换成原始值

关于比较运算符<|>|<=|>=

  1. 如果操作数为对象,那么这个对象将转换成原始值:如果valueof()返回一个原始值,那么直接使用这个原始值。否则,使用toString()的转换结果进行比较操作

  2. 在对象转换成原始值之后,如果两个操作数都是字符串,那么将按照字母表的顺序对两个字符串进行比较,字母表的顺序是指组成这个字符串的16位unicode字符的索引顺序。注意:字符串比较是区分大小写的,所有大写的ASCII字母都小于所有小写的ASCII字母

  3. 在对象转换成原始值之后,如果至少有一个操作数不是字符串,那么两个操作数都将转换成数字进行数值比较。0-0是相等的。Infinity比其他任何数字都大(除了Infinity本身),-Infinity比其他任何数字都要小(除了它本身)。如果其中一个操作数是(或转换后是)NaN,那么比较操作符总是返回false

加号运算符与比较运算符的区别

  1. 加法运算符更偏爱字符串的操作,比较运算符更偏爱数字

  2. 只有两个操作数都是字符串,才进行字符串的比较

1+2 //加法,结果是3
"1"+"2" //字符串连接,结果是"12"
"1"+2  //字符串连接,结果是"12"
11 < 2 //数字的比较,结果是false
"11" < "3" //字符串比较,结果为true
"11" < 3 // 数字比较,"11"转换成11,结果为false
"one" < 3 //数字的比较,"one"转换成NaN,结果为false

注意:<=以及>=在判断相等的情况的时候,并不依赖相等或者严格相等运算符的比较规则,它们只是简单的不大于不小于

反思

昨天公司的一件事对我的触动挺大的,公司里面的两个后台为了表单提交是用GET还是POST争吵了半个小时。有些时候,不经意就会忽略一些问题,比如代码的质量,性能的提高,这些都是值得程序猿们深入探究的问题。

记得有过这样一句话“你对待技术的态度,就是你对待生命的态度”。

作为一名小猿,继续奋斗去喽~


望舒
2.3k 声望133 粉丝

an unexamined life is a life not worth living