值,类型和操作符
值
值表示为二进制位存储在计算机易失内存中,每种值对应一种类型,
数字
JS中数字表示为64位二进制浮点数,遵循IEEE 754标准,0位到51位表示小数部分,52到62表示指数,第63位表示符号位,所以实际的可表示的整数不足2^64个(18后面18个0),大概是9后面15个0,当然也是特别大的。可用9.81
这种表示法,也可以用科学计数法,e后面是指数,如2.998e8
,整数运算在上面整数范围可以保证精确性,小数运算可能是不精确的,小数的存储只能是一种近似的妥协。
这篇文章很好的解释了JS中数字的内部表示numbers represention in javascript
算术运算
包括+-*/%
四种运算,运算顺序取决于优先级,可以加括号改变优先级,乘除取模运算优先级一致。
特殊数字
首先要提到Infinity
和-Infinity
,分别代表正无穷和负无穷,不要过多依赖关于无穷的运算,否则很容易产生下一个特殊数字NaN
,代表非数字(Not a number),0/0
和Infinity-Infinity
这种运算以及任何产生无意义的算术运算都会生成NaN
。
字符串
可用单引号,双引号和反引号表示字符串,只需要对应前后匹配即可。
对于一些特殊字符,比如引号,放在字符串中比较困难。当按下回车键产生的NEWLINE
字符在不转义的情况下只能被包含在反引号中间。
解决方案是用反斜线\
符号转义后面紧跟的字符,比如\n``\t
分别代表换行和tab字符。如果想包含\
在字符串中,需要对\
转义,两个紧挨着的\
在最终的显示结果中只会显示一个\
。下面的例子是“A newline character is written like “\n”.”的字符串表示。
1 | "A newline character is written like \"\\n\"." |
字符串要被编码成二进制数字位存储在计算机中,JS基于Unicode标准实现了字符串的存储。Unicode标准中,每种字符都被编码成一个数字,故此字符串作为字符的组合可以被表示位二进制数字位。但是JS中对字符的表示为16位二进制数字,最多只能表示2^16个字符,但是Unicode定义了差不多两倍多的字符。所以如一些emoji在JS中占据了两个“字符位置”,第五章还会再说。
乘除和减法操作无法运用在字符串上,加法运算是可以的,在字符串环境下表示字符串的拼接。第四章会讲述字符串数据类型的方法。
单引号和双引号包裹的字符串除了内部需要转义的字符不同之外没有其他区别。反引号包裹的字符串,也叫做模板字符串,可以有很多有意思的操作,不仅可以纵跨几行而且可以嵌入其他值。比如
1 | `half of 100 is ${100 / 2}` |
当包含在大括号中一些表达式时,结果会被运算并插入到原位置,对于这个例子的结果是“half of 100 is 50”
。
一元操作符
除了符号表示的操作符,还有些单词表示的操作符,典型的如typeof,生成操作数的类型。涉及两个操作数的操作符叫做二元操作符,一个操作数叫一元操作符。-
既可以是一元操作符也可以是二元操作符。
布尔值
数字比较大小可以产生布尔值,字符串也可以比较大小,比如
1 | console.log("Aardvark" < "Zoroaster") |
字符串的大小比较在JS中的做法是从左至右扫描,比较Unicode码,大写字符要小于小写字母,各种标点符号也被包含在字符串的比较中,并不是严格的按照字典序。
其他比较的操作包括大于等于,小于等于,等于(==)和不等于(!=)。比如
1 | console.log("Itchy" != "Scratchy") |
只有一个值在JS中不等于自己,就是NaN
。NaN
的意义就是表示任何无意义的计算结果,所以不应等于其他无意义的计算结果。
逻辑运算符
包含&&
与||
或!
非。只有非是一元操作符,与或是二元操作符,具有短路效应。
混合数学表达式的时候,不太看得出什么时候需要括号。所以重提优先级,或运算优先级最低,与运算高一点,然后比较运算符更高一点,然后是其他运算符。这样的设计是为了保证像下面的例子中的情况保证括号尽可能的少。
1 | 1 + 1 == 2 && 10 * 10 > 50 |
还有个三元运算符值得注意,就是三目运算符。如
1 | console.log(true ? 1 : 2); |
空值
有两个特殊值是null
和underfined
,被用于表示无意义的值,不带有任何信息。许多操作不产生一个有意义的值都会生成一个underfined因为必须产生一个值。
两者的区别是JS设计的一个意外,大多数情况下没什么区别。
自动类型转换
JS对程序的容错率很高,即便有时候给很怪异的程序,也会正常执行,如下面的代码:
1 | console.log(8 * null) |
当一个运算符被运用到错误的类型时,JS会悄悄地转换成他需要的类型,而且并不是那么像人期望的那样,这叫做类型强转(type coercion)。第一个表达式null转为0,第二个中字符串5转为数字5,第三个数字1转换为字符串1并做了拼接操作。
当有一些并不能显然的转为数字的值被转为数字类型时,就会得到NaN
这个数字类型值,NaN
的后续运算还会一直得到NaN
。
比较不同类型的值的相等性又有着一套令人迷惑的规则。基本规则是转换其中一个值的类型为另外一种类型,然而,当null或者underfined出现在某一方时,当且仅当另一侧也是null或者underfined的时候才会返回true,如
1 | console.log(null == undefined); |
这个特性十分的有用,比如当你想测试一个值是不是有一个真实存在的值而不是null或者underfined,可以把它和null进行比较,用==
或者!=
。
但有时候想确确实实地知道一个值是不是false,这时候三等号就派上了用场,===的比较不会进行类型强转,所以如:false === ''
就会返回false,如果两等号就返回true。
最好用三等号保证类型,除非特别肯定两侧的类型相同。
逻辑运算符的短路效应
逻辑与和逻辑或处理不同类型的值,首先会将左侧的值转为布尔值类型,然后根据操作符的类型决定接下来的动作,要么返回原始的左侧的值或者返回右侧的值。
或运算符,如果左侧可以转为true则返回左侧值,否则返回右侧值。
1 | console.log(null || "user") |
我们可以利用这种特性做一个回退到默认值机制。如果一个值可能是空值,可以让它或上一个替代值。当前者为null或者underfined时,后面的值就会获得。对于数字,只有0和NaN转化为false,对于字符串,只有空字符串转化为false,其余情况都是true。如
1 | 0 || -1 |
逻辑与正好相反,若左边的转为false,直接返回左侧的值,否则返回右侧的值。
还有逻辑与和逻辑或只做必要的求值。对于与运算符,左侧为false就不计算右侧表达式,对于或运算符,左侧为true就不执行右侧表达式计算了。同样的,三目运算符也只是对选中的表达式做计算。插个题外话,SICP中有一道模拟if语句的题目,考察的就是if语句和if函数的差异,if函数会计算所有结果,而if语句只做有必要的求值。
总结
我们学习了四种类型:数字,字符串,未定义值和布尔值。还有一些一元操作符(逻辑非和数字取反),比较运算符,逻辑运算符(与和或),三目运算符等,这一章可以利用JS做个计算器用了,下一张开始用这些写基本的程序。