本节书摘来自异步社区《javascript面向对象编程指南》一书中的第2章,第2.3节,作者: 【加】stoyan stefanov 译者: 凌杰 更多章节内容可以访问云栖社区“异步社区”公众号查看。
我们在程序中所使用的任何值都是有类型的。在javascript中,主要包含以下几大基本数据类型。
1.数字——包括浮点数与整数,例如1、100、3.14。
2.字符串——一序列由任意数量字符组成的序列,例如"a"、"one"、"one 2 three"。
3.布尔值——true或false。
4.undefined——当我们试图访问一个不存在的变量时,就会得到一个特殊值: undefined。除此之外,使用一个未初始化的变量也会如此。因为javascript会自动将变量在初始化之前的值设定为undefined。
5.null——这是另一种只包含一个值的特殊数据类型。所谓的null值,通常是指没有值、空值,不代表任何东西。null与undefined最大的不同在于,被赋予null的变量通常被认为是已经定义了的,只不过它不代表任何东西。关于这一点,我们稍后会通过一些具体的示例来解释。
任何不属于上述五种基本类型的值都会被认为是一个对象。甚至有时候我们也会将null视为对象,这会使人有些尴尬——这是一个不代表任何东西的对象(东西)。我们将会在第4章中深入阐述对象的概念,现在我们只需要记住一点,javascript中的数据类型主要分为以下两个部分。
基本类型(上面列出的五种类型)。
非基本类型(即对象)。
2.3.1 查看类型操作符——typeof
如果我们想知道某个变量或值的数据类型,可以调用一种叫做typeof的特殊操作符。该操作符会返回一个代表数据类型的字符串,它的值包括:“number”、“string”、“boolean”、“undefined”、“object”和“function”。在接下来的几节中,我们将会逐一展示对五种基本数据类型使用typeof操作符时的情况。
2.3.2 数字
最简单的数字类型当然就是整数了。如果我们将一个变量赋值为1,并对其调用typeof操作符,控制台就会返回字符串“number”,请看下面的代码。此外要注意的是,当您第二次设置变量值时,就不需要再使用var语句了。
当然,这也同样适用于浮点数(即含小数部分的数字):
1 除了对变量赋值以外,我们也可以直接对一个数值调用typeof。例如:
2.3.2.1 八进制与十六进制
当一个数字以0开头时,就表示这是一个八进制数。例如,八进制数0377所代表的就是十进制数255。
如您所见,例子中最后一行所输出的就是该八进制数的十进制表示形式。如果您对八进制数还不太熟悉,那么十六进制您一定不会感到陌生,毕竟,css样式表中的颜色值使用的都是十六进制。
在css中,我们定义颜色的方式有以下两种。
使用十进制数分别指定r(红)、g(绿)、b(蓝)的值2,取值范围都为0~255。例如rgb(0,0,0)代表黑色、rgb(255,0,0)代表红色(红值达到最大值,而绿和蓝都为0值)。
使用十六进制数,两个数位代表一种色值,依次是r、g、b。例如#000000代表黑色、#ff0000代表红色,因为十六进制的ff就等于255。
在javascript中,我们会用0x前缀来表示一个十六进制值(简称为hex)。
2.3.2.2 指数表示法
一个数字可以表示成1e1(或者1e+1、1e1、1e+1)这样的指数形式,意思是在数字1后面加1个0,也就是10。同理,2e+3的意思是在数字2后面加3个0,也就是2000。
此外,我们也可以将2e+3理解为将数字2的小数点向右移三位。依照同理,2e-3也就能被理解为是将数字2的小数点左移三位。

2.3.2.3 infinity
在javascript中,还有一种叫做infinity的特殊值。它所代表的是超出了javascript处理范围的数值。但infinity依然是一个数字,我们可以在控制台使用typeof来测试infinity。当我们输入1e308时,一切正常,但一旦将后面的308改成309就出界了。经实践证明,javascript所能处理的最大值是1.7976931348623157e+308,而最小值为5e-324。
另外,任何数除0也为infinity:
infinity表示的是最大数(或者比最大数还要大的数),那么最小数该如何表示呢?答案是在infinity之前加一个负号:
但这是不是意味着我们可以得到双倍的infinity呢?——毕竟我们可以从0加到infinity,也可以从0减到-infinity。好吧,这只是个玩笑。事实上这是不可能的,因为即便将正负infinity相加,我们也不会得到0,而是会得到一个叫做nan(not a number的缩写,即不是数字)的东西。
而且,infinity与其他的任何操作数执行任何算术运算的结果,都是infinity。
2.3.2.4 nan
还记得之前见过的那个nan吗?尽管该值的名字叫做“不是数字”,但事实上它依然属于数字,只不过是一种特殊的数字罢了。
如果我们在对一个假定的数字执行某个操作时失败了,就会得到一个nan。例如,当我们试图将10与字符"f"相乘时,其结果就会为nan,因为"f"显然是不支持乘法运算的。
而且,nan是具有传染性的,只要我们的算术运算中存在一个nan,整个运算就会失败。
2.3.3 字符串
字符串通常指的是一组用于表示文本的字符序列。在javascript中,一对双引号或单引号之间的任何值都会被视为一个字符串。也就是说,1是一个数字的话,"1"就是一个字符串了。在一个字符串上,typeof操作符会返回“string”。
字符串中可以包含数字,例如:
如果引号之间没有任何东西,它所表示的依然是一个字符串(即空字符串):
之前,当我们在两个数字之间使用加号时,所执行的是加法运算。但在字符串中,这是一个字符串拼接操作,它返回的是两个字符串拼接之后的结果。例如:
像+这样的双功能操作符可能会带来一些错误。因此,我们如果想执行拼接操作的话,最好确保其所有的操作数都是字符串。同样的,在执行数字相加时,我们也要确保其所有的操作数都是数字。至于如何做到这一点,我们将会在后续章节中详细讨论。
2.3.3.1 字符串转换
当我们将一个数字字符串用于算术运算中的操作数时,该字符串会在运算中被当做数字类型来使用。(由于加法操作符的歧义性,这条规则不适用于加法运算。)
于是,将数字字符串转换为数字就有了一种偷懒的方法:只需将该字符串与1相乘即可。(当然,更好的选择是调用parseint函数,关于这点,我们将会在下一章中介绍。)
如果转换操作失败了,我们就会得到一个nan值。
此外,将其他类型转换为字符串也有一种偷懒的方法,只需要将其与空字符串连接即可:
2.3.3.2 特殊字符串
在表2-2中,我们列出了一些具有特殊含义的字符串。
除此之外,还有一些很少被使用的特殊字符,例如:b(退格符)、v(纵向制表符)、f(换页符)等。
2.3.4 布尔值
布尔类型中只有两种值:true和false。它们可用于引号以外的任何地方。
如果true或false在引号内,它就是一个字符串。
2.3.4.1 逻辑运算符
在javascript中,主要有三种逻辑运算符,它们都属于布尔运算。分别是:
!——逻辑非(取反)。
&&——逻辑与。
||——逻辑或。
在javascript中,如果我们想描述一些日常生活中非真即假的事物,就可以考虑使用逻辑非运算符:
如果在同一个值上执行两次逻辑非运算,其结果就等于原值3:
如果在一个非布尔值上执行逻辑运算,该值会在计算期间被转换为布尔值:
如您所见,上例中的字符串"one"是先被转换为布尔值true然后再取反的,结果为false。如果我们对它取反两次,结果就会为true。例如:
使用双重取反操作可以很容易地将任何值转换为等效的布尔值。虽然这种方法很少被用到,但从另一个角度也说明了将其他类型的值转换为布尔值的重要性。而事实上,除了下面所列出特定值以外(它们将被转换为false),其余大部分值在转换为布尔值时都为true。
空字符串""
null
undefined
数字0
数字nan
布尔值false
这6个值有时也会被我们统称为falsy,而其他值则被称为truthy(包括字符串"0"、""、"false")。
接下来,让我们来看看另外两个操作符——逻辑与和逻辑或的使用示例。当我们使用逻辑与操作符时,当且仅当该操作所有操作数为true时,它才为true。而逻辑或操作则只需要至少一个操作数为true即可为true。
在表2-3中,我们列出了所有可能的情况及其相应结果。
当然,我们也能连续执行若干个逻辑操作。例如:
我们还可以在同一个表达式中混合使用&&和||。不过在这种情况下,我们最好用括号来明确一下操作顺序。例如:
2.3.4.2 操作符优先级
您可能会想知道,为什么上例中的第一个表达式(false && false || true && true)结果为true。答案在于操作符优先级。这看上去有点像数学,例如:
由于乘法运算的优先级高于加法,所以该表达式会先计算2<code>*</code> 3,这就相当于我们输入的表达式是:
逻辑运算符也一样,!的优先级最高,因此在没有括号限定的情况下它将会被最先执行。然后,接下来的优先顺序是先&&后||。也就是说:
与下面表达式等效:
最佳方法:
尽量使用括号,而不是依靠操作符优先级来设定代码的执行顺序,这样我们的代码才能有更好的可读性。
2.3.4.3 惰性求值
如果在一个连续的逻辑操作中,操作结果在最后一个操作完成之前就已经明确了的话,那么该操作往往就不必再继续执行了,因为这已经不会对最终结果产生任何影响。例如,在下面这种情况中:
在这里,所有的逻辑或运算符优先级都是相同的,只要其中任何一个操作数为true,该表达式的结果就为true。因而当第一个操作数被求值之后,无论后面的值是什么,结果都已经被确定了。于是我们可以允许javascript引擎偷个懒(好吧,这也是为了提高效率),在不影响最终结果的情况下省略一些不必要的求值操作。为此,我们可以在控制台中做个实验:
除此之外,上面的例子还向我们显示了另一个有趣的事情——如果javascript引擎在一个逻辑表达式中遇到一个非布尔类型的操作数,那么该操作数的值就会成为该表达式所返回的结果。例如:
通常情况下,这种行为是应该尽量避免的,因为它会使我们的代码变得难以理解。但在某些时候这样做也是有用的。例如,当我们不能确定某个变量是否已经被定义时,就可以像下面这样,即如果变量mynumber已经被定义了,就保留其原有值,否则就将它初始化为10。
这种做法简单而优雅,但是请注意,这也不是绝对安全的。如果这里的mynumber之前被初始化为0(或者是那6个falsy值中的任何一个),这段代码就不太可能如我们所愿了。
2.3.4.4 比较运算符
在javascript中,还有另外一组以布尔值为返回值类型的操作符,即比较操作符。下面让我们通过表2-4来了解一下它们以及相关的示例。
还有一件有趣的事情要提醒读者注意:nan不等于任何东西,包括它自己。
2.3.5 undefined与null
通常情况下,当我们试图访问某个不存在的或者未经赋值的变量时,就会得到一个undefined值。javascript会自动将声明时没有进行初始化的变量设为undefined。
当我们试图使用一个不存在的变量时,就会得到这样的错误信息:
这时候,如果我们在该变量上调用typeof操作符,就会得到字符串“undefined”:
如果我们声明一个变量时没有对其进行赋值,调用该变量时并不会出错,但typeof操作符依然会返回“undefined”。
而null值就完全是另一回事了。它不能通过javascript来自动赋值,只能通过我们的代码来完成。
尽管undefined和null之间的差别微乎其微,但有时候也很重要。例如,当我们对其分别执行某种算术运算时,结果就会截然不同:
这是因为null和undefined在被转换为其他基本类型时,方法存在一定的区别,下面我们给出一些可能的转换类型。
转换成数字:
转换成布尔值:
转换成字符串: