Vue 与 CSS变量.png

Undefined类型

Undefined类型只有一个唯一的字面值undefined,表示的是一个变量不存在。
下面是4种常见的出现undefined的场景。
① 使用只声明而未初始化的变量时,会返回“undefined”

  1. var a;
  2. console.log(a); // undefined

② 获取一个对象的某个不存在的属性(自身属性和原型链继承属性)时,会返回“undefined”。

  1. var obj = {
  2. name: 'xjp'
  3. }
  4. console.log(obj.age) // undefined

③ 函数没有明确的返回值时,却在其他地方使用了返回值,会返回“undefined”。

  1. function test() {}
  2. console.log(test()) // undefined

④ 函数定义时使用了多个形参,而在调用时传递的实参数量少于形参数量,那么未匹配上的参数就为“undefined”。

  1. function test(param1, param2, param3) {
  2. console.log(param3)
  3. }
  4. test(1, 2) // undefined

Null类型

Null类型只有一个唯一的字面值null,表示一个空指针对象,这也是在使用typeof运算符检测null值时会返回“object”的原因。
下面是3种常见的出现null的场景。
① 一般情况下,如果声明的变量是为了以后保存某个值,则在声明时将其赋值为“null”。

  1. var obj = null
  2. function foo() {
  3. return {
  4. name: 'xjp'
  5. }
  6. }
  7. obj = foo()

② JavaScript在获取DOM元素时,如果没有获取到指定的元素对象,就会返回“null”。

  1. document.querySelector('#id') // null

③ 在使用正则表达式进行捕获时,如果没有捕获结果,就会返回“null”。

  1. 'test'.match(/a/) // null

Undefined和Null的异同

相同点

  • Undefined和Null两种数据类型都只有一个字面值,分别是undefined和null
  • Undefined类型和Null类型在转换为Boolean类型的值时,都会转换为false。所以通过非运算符(!)获取结果为true的变量时,无法判断其值为undefined还是null
  • 在需要将两者转换成对象时,都会抛出一个TypeError的异常,也就是平时最常见的引用异常 ```javascript var a; var b = null;

console.log(a.name) // Cannot read property ‘name’ of undefined console.log(b.name) // Cannot read property ‘name’ of null

  1. - **Undefined类型派生自Null类型**,所以在非严格相等的情况下,两者是相等的`null == undefined // true`
  2. **不同点**
  3. - **nullJavaScript中的关键字,而undefinedJavaScript中的一个全局变量(内置属性),即挂载在window对象上的一个变量,并不是关键字(也不能作为 Javascript 的变量或函数名)。**
  4. - 在使用typeof运算符检测时,Undefined类型的值会返回“undefined”,而Null类型的值会返回“object”。
  5. - 在通过call调用toString()函数时,Undefined类型的值会返回“[object Undefined]”,而Null类型的值会返回“[object Null]”。
  6. - 在需要进行字符串类型的转换时,null会转换为字符串"null",而undefined会转换为字符串"undefined"
  7. - 在需要进行数值类型的转换时,undefined会转换为NaN,无法参与计算;null会转换为0,可以参与计算。
  8. - 无论在什么情况下都没有必要将一个变量显式设置为undefined。如果需要定义某个变量来保存将来要使用的对象,应该将其初始化为null。这样不仅能将null作为空对象指针的惯例,还有助于区分nullundefined
  9. <a name="oUUVu"></a>
  10. # Boolean类型
  11. Boolean类型(又称布尔类型)的字面值只有两个,分别是truefalse,它们是区分大小写的,其他值(如TrueFalse),并不是Boolean类型的值。
  12. Boolean类型使用最多的场景就是用于if语句判断。在JavaScript中,if语句可以接受任何类型的表达式,即if(a)语句中的a,可以是BooleanNumberStringObjectFunctionNullUndefined中的任何类型。
  13. 如果a不是Boolean类型的值,那么JavaScript解释器会自动调用Boolean()函数对a进行类型转换,返回最终符合if语句判断的true或者false值。
  14. <a name="jDMBC"></a>
  15. ## 不同类型转换为Boolean类型
  16. - **String类型转换为Boolean类型**
  17. - 空字符串`""`或者`''`都会转换为false
  18. - 任何非空字符串都会转换为true,包括只有空格的字符串`" "`
  19. - **Number类型转换为Boolean类型**
  20. - `0``NaN`会转换为false
  21. - 除了0NaN以外,都会转换为true,包括表示无穷大和无穷小的Infinity和-Infinity
  22. - **Object类型转换为Boolean类型**
  23. - objectnull时,会转换为false
  24. - 如果object不为null,则都会转换为true,包括空对象`{}`
  25. - **Function类型转换为Boolean类型**
  26. - 任何Function类型的值都会转换为true
  27. - **Null类型转换为Boolean类型**
  28. - Null类型只有一个null值,会转换为false
  29. - **Undefined类型转换为Boolean类型**
  30. - Undefined类型只有一个undefined值,会转换为false
  31. <a name="muUPt"></a>
  32. # Number类型
  33. JavaScript中,Number类型的数据既包括了**整型数据**,也包括了**浮点型数据**,它们都统一采用64位浮点数进行存储。
  34. <a name="KiuVJ"></a>
  35. ## 整型数据
  36. 最基本的数值采用的是十进制整数,另外,数值还可以通过八进制或者十六进制表示。<br />**① 八进制。**如果想要用八进制表示一个数值,那么首位必须是0,其他位必须是07的八进制序列。如果后面位数的字面值大于7,则破坏了八进制数据表示规则,前面的0会被忽略,当作十进制数据处理。
  37. ```javascript
  38. var num1 = 024 // 4+2*8=20 八进制
  39. var num2 = 079 //79 最后一位9超出了八进制字面值,当十进制处理

② 十六进制。如果想要用十六进制表示一个数值,那么前两位必须是0x,其他位必须是十六进制序列(0~9,a~f或者A~F)。如果超过了十六进制序列,则会抛出异常。

  1. var num3 = 0x3f // 15+3*16=63
  2. var num4 = 0x2g // SyntaxError: Invalid or unexpected token

浮点型数据

经典问题:0.1+0.2 为什么不等于0.3?
原因:一个浮点型数它总长度是64位,其中最高位为符号位,接下来的11位为指数位,最后的52位为小数位,即有效数字的部分

  • 第0位:符号位sign表示数的正负,0表示正数,1表示负数。
  • 第1位到第11位:存储指数部分,用e表示。
  • 第12位到第63位:存储小数部分(即有效数字),用f表示。

image.png
因为浮点型数使用64位存储时,最多只能存储52位的小数位,对于一些存在无限循环的小数位浮点数,会截取前52位,从而丢失精度,所以会出现上面实例中的结果。
计算过程:0.1 + 0.2 = 0.30000000000000004的运算为例,首先将各个浮点数的小数位按照“乘2取整,顺序排列”的方法转换成二进制表示。

  1. // 用2乘以浮点数,将积的整数部分取出;然后再用2乘以余下的小数部分,又得到一个积;再将积的整数部分取出,如此推进,直到积中的小数部分为零为止。
  2. 0.1*2=0.2 // 取整数部分0
  3. 0.2*2=0.4 // 取整数部分0
  4. 0.4*2=0.8 // 取整数部分0
  5. 0.8*2=1.6 // 取整数部分1
  6. 0.6*2=1.2 // 取整数部分1
  7. 0.2*2=0.4 // 取整数部分0
  8. 0.4*2=0.8 // 取整数部分0
  9. 0.8*2=1.6 // 取整数部分1
  10. //···无限循环

因此0.1转换成二进制表示为0.0 0011 0011 0011 0011 0011 0011……(无限循环)。同理对0.2进行二进制的转换结果为0.0011 0011 0011 0011 00110011……(无限循环)。
将0.1与0.2相加,然后转换成52位精度的浮点型表示。image.png
得到的结果为0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 11001100 1100,转换成十进制值为0.30000000000000004。
解决方案:将浮点数先乘以一定的数值(10、100、100·····)转换为整数,通过整数进行运算,然后将结果除以相同的数值转换成浮点数后返回。

  1. const operationObj = {
  2. // 处理传入的参数,不管传入的是数组还是以逗号分隔的参数都处理为数组
  3. handleParams(args) {
  4. return Array.prototype.concat.apply([], args)
  5. },
  6. /** 乘数因子,根据小数位数计算
  7. *1、首先判断有无小数点,无返回1
  8. *2、有小数点,将第一个小数点后的位数长度作为Math.pow()函数的参数进行计算,如2.01乘数因子为100
  9. */
  10. multiplier(x){
  11. let parts = x.toString().split('.')
  12. return parts.length < 2?1:Math.pow(10, parts[1].length)
  13. },
  14. /** 获取多个数据中最大的乘数因子
  15. *例如1.3乘数因子为10,2.01乘数因子为100,则最大乘数因子为100
  16. */
  17. selectMaxFactor() {
  18. let args = Array.prototype.slice.call(arguments)
  19. let argArr = this.handleParams(args)
  20. return argArr.reduce((accum, next) => {
  21. let num = this.multiplier(next);
  22. return Math.max(accum, num)
  23. }, 1)
  24. },
  25. // 加法运算
  26. add(...args) {
  27. let calArr = this.handleParams(args);
  28. let maxFactor = this.selectMaxFactor(calArr) // 获取参与运算值的最大乘数因子
  29. let sum = calArr.reduce((accum, next) => {
  30. // 将浮点数乘以最大因子,转成整数参与运算
  31. return accum + Math.round(next*maxFactor)
  32. }, 0)
  33. return sum / maxFactor // 最后再除以最大乘数因子
  34. },
  35. // 减法运算
  36. subtract(...args){
  37. let calArr = this.handleParams(args);
  38. let maxFactor = this.selectMaxFactor(calArr)
  39. let diff = calArr.reduce((accum, next, index) => {
  40. // reduce()函数在未传入初始值时,index从1开始,第一位参与运算的值需要乘最大因子
  41. if(index === 1) {
  42. return Math.round(accum*maxFactor) - Math.round(next*maxFactor)
  43. }
  44. // accum作为上一次运算的结果,不需要再乘最大因子
  45. return Math.round(accum) - Math.round(next*maxFactor)
  46. })
  47. return diff / maxFactor
  48. },
  49. // 乘法运算
  50. multiply(...args){
  51. let calArr = this.handleParams(args);
  52. let maxFactor = this.selectMaxFactor(calArr)
  53. calArr = calArr.map(item => item*maxFactor)
  54. let multi = calArr.reduce((accum, next) => {
  55. return Math.round(accum)*Math.round(next)
  56. }, 1)
  57. return multi / Math.pow(maxFactor, calArr.length)
  58. },
  59. // 除法运算
  60. divide(...args) {
  61. let calArr = this.handleParams(args);
  62. let quotient = calArr.reduce((accum, next) => {
  63. let maxFactor = this.selectMaxFactor(accum, next)
  64. return Math.round(accum*maxFactor)/Math.round(next*maxFactor)
  65. })
  66. return quotient
  67. }
  68. }
  69. console.log(operationObj.add(0.1, 0.7))
  70. console.log(operationObj.subtract(0.4, 0.6))
  71. console.log(operationObj.multiply(0.8, 123))
  72. console.log(operationObj.divide(0.3, 0.1))

Number类型转换

在JavaScript中,一共有3个函数可以将其他类型的值转换为Number类型的,分别是**Number()函数****parseInt()函数****parseFloat()函数**

Number()函数

Number()函数可以用于将任何类型转换为Number类型,它在转换时遵循下列规则。
① 如果是数字,会按照对应的进制数据格式,统一转换为十进制并返回。

  1. Number(10) // 10
  2. Number(010) // 8
  3. Number(0x10) // 16

② Boolean类型转换为Number类型,true将返回为“1”,false将返回为“0”。

  1. Number(true) // 1
  2. Number(false) // 0

③Null类型转换为Number类型,则返回“0”。

  1. Number(null) // 0

④ Undefined类型转换为Number类型,则返回“NaN”。

  1. Number(undefined) // NaN

String类型转换为Number类型,则遵循下列规则:

  • 如果是空字符串或为连续多个空格,则转换为0
  • 如果字符串中只包含数字,则会转换成十进制数;如果前面有0,会直接省略掉,例如”0123”会转换为123
  • 如果字符串中包含的是有效的浮点数,则同样按照十进制转换,前置的多个重复的0会被清空,只保留一个,例如”00.23”会转换为0.23
  • 如果字符串中包含有效的十六进制格式,则会按照十进制转换,例如”0x3f”会转换为63
  • 如果字符串是有效的八进制形式,则不会按照八进制转换,而是直接按照十进制转换并输出,因为前置的0会被直接忽略。
  • 如果字符串中包含了除上述格式以外的字符串,则会直接转换为NaN

    1. Number('') // 0
    2. Number(' ') // 0
    3. Number('0123') // 123
    4. Number('00.12') // 0.12
    5. Number('0x21') // 33
    6. Number('010') // 10 八进制直接按照十进制输出
    7. Number('123a') // NaN

    ⑥object类型转换为Number类型

  • Object类型在转换为Number类型时,会优先调用valueOf()函数,然后通过valueOf()函数的返回值按照上述规则进行转换:

    • 如果转换的结果是NaN,则调用toString()函数,通过toString()函数的返回值重新按照上述规则进行转换
    • 如果有确定的Number类型返回值,则结束,否则返回“NaN” ```javascript // 通过valueOf()函数将对象正确转换成Number类型的示例 var obj = { age: 21, valueOf:function(){ return this.age; }, toString:funvtion(){ return ‘good’; } }

Number(obj) // 21

// 通过toString()函数将对象正确转换成Number类型的示例 var obj2 = { age: ‘21’, valueOf:function(){ return []; }, toString:funvtion(){ return this.age; } }

Number(obj2) // 21

// 通过valueOf()函数和toString()函数都无法将对象转换成Number类型的示例(最后返回“NaN”) var obj3 = { age: ‘21’, valueOf:function(){ return ‘a’; }, toString:function(){ return ‘b’; } }

Number(obj3) // NaN

// 如果toString()函数和valueOf()函数返回的都是对象类型而无法转换成基本数据类型,则会抛出类型转换的异常。

var obj4 = { age: ‘21’, valueOf:function(){ return []; }, toString:function(){ return []; } }

Number(obj4) // TypeError: Cannot convert object to primitive value

  1. <a name="nXnnn"></a>
  2. ### parseInt()函数
  3. parseInt()函数用于解析一个字符串,并返回指定的基数对应的**整数值**。如果该字符串无法转换成Number类型,则会返回“NaN”。<br />语法格式:`parseInt(string, radix)`
  4. - 参数`string`表示要被解析的值,如果该参数不是一个字符串,那么会使用toString()函数将其转换成字符串,而字符串前面的空白符会被忽略。
  5. - `radix`表示的是进制转换的基数,数据范围是**2~36**,可以是使用频率比较高的二进制、十进制、八进制和十六进制等,默认值为10。因为对相同的数采用不同进制进行处理时可能会得到不同的结果,所以在任何情况下使用parseInt()函数时,建议都手动补充第二个表示基数的参数。
  6. - 任何整数以0为基数取整时,都会返回本身;不满足基数的整数在处理后会返回“NaN”
  7. 1. **非字符串类型转换为字符串类型**
  8. ```javascript
  9. parseInt('0x12', 16) // 18 直接将字符串"0x12"转换为十六进制数
  10. parseInt(0x12, 16) // 24 由于传入的是十六进制数,先转成十进制18,再转成字符串"18",再将字符串"18"转换成十六进制数
  1. 数据截取的前置匹配原则

parseInt()函数在做转换时,对于传入的字符串会采用前置匹配的原则。即从字符串的第一个字符开始匹配,如果处于基数指定的范围,则保留并继续往后匹配满足条件的字符,直到某个字符不满足基数指定的数据范围,则从该字符开始,舍弃后面的全部字符。在获取到满足条件的字符后,将这些字符转换为整数。

  1. parseInt('fg123', 16) // 15

对于字符串'fg123',首先从第一个字符开始,'f'是满足十六进制的数据,因为十六进制数据范围是0~9a~f(A~F),所以保留'f';然后是第二个字符'g',它不满足十六进制数据范围,因此从第二个字符至最后一个字符全部舍弃,最终字符串只保留字符'f';然后将字符'f'转换成十六进制的数据,为15,因此最后返回的结果为“15”。
如果遇到的字符串是以”0x”开头的,那么在按照十六进制处理时,会计算后面满足条件的字符串;如果按照十进制(或八进制)处理,则会直接返回“0”。

  1. parseInt('0x12', 16) // 2+1*16=18
  2. parseInt('0x12', 10) // 0
  3. parseInt('0x12', 8) // 0

需要注意的一点是,如果传入的字符串中涉及算术运算,则不执行,算术符号会被当作字符处理;如果传入的参数是算术运算表达式,则会先运算完成得到结果,再参与parseInt()函数的计算。

  1. parseInt('15*3', 10) // 15 直接当字符串处理,不会进行算术运算
  2. parseInt(15*3, 10) // 45 先运算得到45,再进行parseInt(45, 10)的运算
  1. 对包含字符e的不同数据的处理差异

当传入的参数本身就是Number类型时,会将e按照科学计数法计算后转换成字符串,然后按照对应的基数转换得到最终的结果。
如果传入的字符串中直接包含e,那么并不会按照科学计数法处理,而是会判断字符e是否处在可处理的进制范围内,如果不在则直接忽略,如果在则转换成对应的进制数。

  1. parseInt(6e3, 10) // 6*10的三次方=6000
  2. parseInt(6e3, 16) // 24576 实际执行parseInt('6000', 16)
  3. parseInt('6e3', 10) // 6 字符'e'不在十进制所能表达的范围内,所以会直接省略
  4. parseInt('6e3', 16) // 3+14*16+6*16*16=1763
  1. 对浮点型数的处理

如果传入的值是浮点型数,则会忽略小数点及后面的数,直接取整。

  1. parseInt('6.01', 10) // 6
  2. parseInt('6.99', 16) //6

练习:

  1. parseInt('0xF', 16)
  2. parseInt('F', 16)
  3. parseInt('fxx123', 16)
  4. parseInt('12', 13)
  5. parseInt('015', 10)
  6. parseInt(15.99, 10)
  7. parseInt('15,123', 10)
  8. parseInt('15*2', 10)
  9. parseInt('15e2', 10)
  10. parseInt('15px', 10)
  11. parseInt('17', 8)
  12. parseInt(021, 8)
  13. parseInt('1111', 2)
  1. map()函数与parseInt()函数的隐形坑

情景:将一个数组[‘1’,’2’, ‘3’, ‘4’],全部转换为整数,调用Array的map()函数中调用parseInt()函数

  1. var arr = ['1', '2', '3', '4']
  2. var result = arr.map(parseInt)
  3. console.log(result)

结果:得到的结果是[1, NaN, NaN, NaN],与我们期望的结果[1, 2, 3, 4]差别很大,这是为什么呢?
解析:

  1. // arr.map(parseInt)等效于
  2. arr.map(function(val, index){
  3. return parseInt(val, index)
  4. })
  5. // parseInt()函数接收的第二个参数实际为数组的索引值,所以实际执行的是
  6. parseInt('1', 0) // 1 任何整数以0为基数取整时,都会返回本身
  7. parseInt('2', 1) // NaN parseInt对应的基数只能为2~36,不满足基数的整数在处理后会返回“NaN”
  8. parseInt('3', 2) // NaN 二进制时只有0和1,3超出了二进制的表示范围
  9. parseInt('4', 3) // NaN 同上

解决:map()函数中使用parseInt()函数时需要注意不能直接将parseInt()函数作为map()函数的参数,而是需要在map()函数的回调函数中使用,并尽量指定基数

  1. var arr = ['1', '2', '3', '4']
  2. var result = arr.map(item => parseInt(item, 10))
  3. console.log(result) // [1,2,3,4]

parseFloat()函数

parseFloat()函数用于解析一个字符串,返回对应的浮点数。如果给定值不能转换为数值,则会返回“NaN”
parseInt()函数相比,parseFloat()函数没有进制的概念,只解析十进制,因此不能指定底数。
如果在解析过程中遇到了正负号(+ / -)数字0~9小数点或者科学计数法(e / E)以外的字符则会忽略从该字符开始至结束的所有字符,然后返回当前已经解析的字符的浮点数形式。其中,正负号必须出现在字符的第一位,而且不能连续出现。

  1. parseFloat('+1.2') // 1.2
  2. parseFloat('-1.2') // -1.2
  3. parseFloat('++1.2') // NaN 符号不能连续出现
  4. parseFloat('--1.2') // NaN 符号不能连续出现
  5. parseFloat('1+1.2') // 1 +不是出现在第一位,不会当符号位处理,直接舍弃及之后

② 字符串前面的空白符会直接忽略,如果第一个字符就无法解析,则会直接返回“NaN”。

  1. parseFloat(' 1.2') // 1.2
  2. parseFloat('f1.2') // NaN

③ 对于字符串中出现的合法科学运算符e,会先进行运算处理然后转换成浮点型数,这点与parseInt()函数的处理有很大的不同。

  1. parseFloat('4e3') // 4000 先进行科学计数法运算
  2. parseInt('4e3', 10) // 4 不进行运算,直接从第一个字符开始匹配

④ 对于小数点,只能正确匹配第一个,第二个小数点是无效的,它后面的字符也都将被忽略。

  1. parseFloat('11.20') // 11.2
  2. parseFloat('11.2.1') // 11.2

练习:

  1. parseFloat('123AF')
  2. parseFloat('0xa')
  3. parseFloat('22.5')
  4. parseFloat('00.3.56')
  5. parseFloat('0908.5')

比较

  • Number()函数转换的是传入的整个值,并不是像parseInt()函数和parseFloat()函数一样会从首位开始匹配符合条件的值。如果整个值不能被完整转换,则会返回“NaN”。
  • parseFloat()函数在解析小数点时,会将第一个小数点当作有效字符,而parseInt()函数在解析时如果遇到小数点会直接停止,因为小数点不是整数的一部分。
  • parseFloat()函数在解析时没有进制的概念,而parseInt()函数在解析时会依赖于传入的基数做数值转换。

    isNaN()函数与Number.isNaN()函数对比

    在判断NaN时,ES5提供了全局的isNaN()函数,而ES6为Number类型增加了静态函数isNaN()

NaN含义:Number类型数据中存在一个比较特殊的数值NaN(Not a Number),它表示应该返回数值却并未返回数值的情况。
NaN存在的目的:是在某些异常情况下保证程序的正常执行。例如0/0,在其他语言中,程序会直接抛出异常,而在JavaScript中会返回“NaN”,程序可以正常执行。
NaN特点:第一个是任何涉及NaN的操作都会返回“NaN”,第二个是NaN与任何值都不相等,即使是与NaN本身相比。NaN == NaN // false
NaN产生的条件:

  1. 一方面是在数据运算时,返回了一个无法表示的数值,例如0 / 0就会返回“NaN”。有一点需要注意的是除了0 / 0,其他数据除以0都返回“Infinity”。
  2. 另一方面是在需要做强制类型转换时,某些数据不能直接转换为数值类型,就会返回“NaN”,例如1 - ‘a’ = NaN,因为字符串’a’无法参与数值运算。

    isNaN()函数

    作用:它用来确定一个任意类型变量是不是NaN。NaN是一个Number类型的数值,只不过这个值无法用真实的数字表示。
    问题:如果传递的参数是Number类型数据,可以很容易判断是不是NaN。如果传递的参数是非Number类型,它返回的结果往往会让人费解:isNaN({}) // true
    空对象{}明明不是一个NaN的数据,应该返回的是“false”,为什么会返回“true”呢?
    执行:isNaN()函数会先对传入的变量进行类型转换,去判断值能否转换为数字,如果能转换成数字则会返回“false”,如果无法转换则会返回“true”。
    1. isNaN(NaN) // true 任何涉及NaN的操作都会返回“NaN”
    2. isNaN(undefined) // true Number(undefined)转换成数字为NaN
    3. isNaN({}) // true
    4. isNaN(true) // false Number(true)转换成数字为1
    5. isNaN(null) // false Number(null)转换成数字为0
    6. isNaN(1) // false
    7. isNaN('') // false Number('')转换成数值为0
    8. isNaN(' ') // false 同上
    9. isNaN('1') // false Number('1')转换成数字1
    10. isNaN('js') // true
    11. isNaN(new Date()) // false Number(new Date())会转换为对应的时间戳如1623223946176
    12. isNaN(new Date().toString()) // true 字符串表示的时间,不是字符串表示的时间戳,无法转换

    Number.isNaN()函数

    isNaN()函数本身存在误导性,而ES6中的Number.isNaN()函数会在真正意义上去判断变量是否为NaN,不会做数据类型转换只有在传入的值为NaN时,才会返回“true”,传入其他任何类型的值时会返回“false”。
    1. Number.isNaN(NaN) // true
    2. Number.isNaN(0/0) // true
    3. Number.isNaN(undefined) // false
    4. Number.isNaN(null) // false
    5. Number.isNaN(true) // false
    6. Number.isNaN('') // false
    7. Number.isNaN(123) // false
    兼容处理:在非ES6环境中使用ES6中的isNaN()函数
    1. if(!Number.isNaN) {
    2. Number.isNaN = function(n) {
    3. return n!==n // 只有在n为NaN的时候才返回“true”,
    4. }
    5. }
    因为在所有类型的数据中,如果一个变量和自身作比较,只有在变量值为NaN时才会返回“false”,其他情况都是返回“true”。

    总结

    isNaN()函数与Number.isNaN()函数的差别主要是对传入的值是否进行数据类型转换:
  • isNaN()函数在判断是否为NaN时,需要先进行数据类型转换,只有在无法转换为数字时才会返回“true”;
  • Number.isNaN()函数在判断是否为NaN时,只需要判断传入的值是否为NaN,并不会进行数据类型转换。

    String类型

    JavaScript中的String类型(字符串类型)既可以通过双引号””表示,也可以通过单引号’’表示,而且是完全等效的,这点与Java、PHP等语言在字符串的处理上是不同的。

在JavaScript中,有3种定义字符串的方式,分别是字符串字面量直接调用String()函数new String()构造函数

字符串字面量

含义:字符串字面量就是直接通过单引号或者双引号定义字符串的方式。只不过使用单引号开头的字符串就要使用单引号结尾,使用双引号开头的字符串就要使用双引号结尾。

  1. var str = 'xjp' // 正确写法
  2. var str = "xjp" // 正确写法
  3. var str = 'xjp" // 错误写法,首尾符号不一样

直接调用String()函数

直接调用String()函数,会将传入的任何类型的值转换成字符串类型,在转换时遵循的规则如下。
① 如果是基本数据类型,则会直接将字面值转换为字符串表示形式。

  1. String(123) // '123'
  2. String(123.12) // '123.12'
  3. String(true) // 'true'
  4. String(false) // 'false'
  5. String(null) // 'null'
  6. String(undefined) // 'undefined'
  7. String('this is a string') // 'this is a string' 如果值为字符串,则直接返回字符串本身

②如果值为引用类型,则会先调用**toString()**函数获取返回值,如果返回值是基本数据类型则转换字符串类型,如果不是,则再调用对象的**valueOf()**函数获取返回值,如果返回值是基本数据类型则转换字符串类型,如果还满足,则会抛出类型转换的异常。

  1. // 通过toString()函数将对象正确转换成String类型的示例
  2. var obj = {
  3. age: 21,
  4. valueOf:function(){
  5. return this.age;
  6. },
  7. toString:funvtion(){
  8. return 'good';
  9. }
  10. }
  11. String(obj) // 'good'
  12. // 通过valueOf()函数将对象正确转换成String类型的示例
  13. var obj2 = {
  14. age: '21',
  15. valueOf:function(){
  16. return this.age;
  17. },
  18. toString:function(){
  19. return [];
  20. }
  21. }
  22. String(obj2) // '21'
  23. // 如果toString()和valueOf()返回的都是对象类型而无法转换成原生类型时,则会抛出类型转换的异常
  24. var obj3 = {
  25. age: '21',
  26. valueOf:function(){
  27. return [];
  28. },
  29. toString:function(){
  30. return [];
  31. }
  32. }
  33. String(obj3) // TypeError: Cannot convert object to primitive value

更简单的转换方法:直接使用加号(+)拼接一个空字符串(””)。

  1. console.log(123 + '') // '123'
  2. console.log([1, 2, 3] + '') // '1,2,3'
  3. console.log(true + '') // 'true'

new String()构造函数

**new String()**构造函数使用new运算符生成String类型的实例,对于传入的参数同样采用和上述String()函数一样的类型转换策略,最后的返回值是一个String类型对象的实例

  1. new String('xjp') // String {"xjp"}

三者的区别

  • 第一种字**符串字面量**方式和第二种直接调用**String()**函数的方式得到的字符串都是基本字符串,而第三种方式**new String()**生成的字符串是字符串对象
  • 基本字符串在作比较时,只需要比较字符串的值即可;而在比较字符串对象时,比较的是对象所在的地址。 ```javascript var str = ‘xjp’; // 基本字符串 var str2 = String(str); // 基本字符串 var str3 = String(‘xjp’); // 基本字符串 var str4 = new String(str); // 基本字符串 var str5 = new String(str); // String类型对象的实例 var str6 = new String(‘xjp’); // String类型对象的实例

str === str2 // true 都是基本字符串 str2 === str3 // true 都是基本字符串 str4 === str5 // false 都是在内存中新生成的地址,彼此各不相同 str5 === str6 // false 都是在内存中新生成的地址,彼此各不相同 str3 === str4 // false 基本字符串和字符串对象的比较,在判断严格相等时,返回’false’ str3 == str4 // true 在不严格下返回’true’

  1. <a name="fNlYd"></a>
  2. ## 函数的调用
  3. `**eval()函数**`会将基本字符串作为源代码处理,如果涉及表达式会直接进行运算,返回运算后的结果;而字符串对象则会被看作对象处理,返回对象本身。
  4. ```javascript
  5. var s1 = '2 + 2' // 创建一个字符串字面量
  6. var s2 = new String('2 + 2') // 创建一个对象字符串
  7. console.log(eval(s1)) // 4
  8. console.log(eval(s2)) // String{'2 + 2'}

toString()与valueOf()

在JavaScript中,**toString()**函数与**valueOf()**函数解决的是值的显示和运算的问题,所有引用类型都拥有这两个函数。

toString()函数

作用:toString()函数的作用是把一个逻辑值转换为字符串,并返回结果。
引用类型的默认实现

  • **Object**类型数据的toString()函数默认的返回结果是**"[object Object]"**,当我们自定义新的类时,可以重写toString()函数,返回可读性更高的结果。
  • **Array**的toString()函数返回值为以逗号分隔构成的数组成员字符串,例如[1,2,3].toString()结果为字符串’1,2,3’
  • **Function**的toString()函数返回值为函数的文本定义,例如(function(x){return x2;}).toString()的结果为字符串”function(x){return x 2;}”
  • **Date**的toString()函数返回值为具有可读性的时间字符串,例如,newDate().toString()的结果为字符串”Sun Nov 25 2018 15:00:16 GMT+0800 (中国标准时间)”

使用
一个引用类型的数据在转换为**String**类型时:

  • 如果对象具有toString()函数,则会优先调用toString()函数。如果它返回的是一个原始值,则会直接将这个原始值转换为字符串表示,并返回该字符串。
  • 如果对象没有toString()函数,或者toString()函数返回的不是一个原始值,则再去调用valueOf()函数,如果valueOf()函数返回的结果是一个原始值,则会将这个结果转换为字符串表示,并返回该字符串。
  • 如果通过toString()函数或者valueOf()函数都无法获得一个原始值,则会直接抛出类型转换异常 ```javascript var arr = [] arr.toString = function(){ console.log(‘执行了toString’) return [] } arr.valueOf = function(){ console.log(‘执行了valueOf’) return [] }

console.log(String(arr)) // 执行了toString // 执行了valueOf // TypeError: Cannot convert object to primitive value

  1. <a name="pd1TH"></a>
  2. ## valueOf()函数
  3. **作用**:valueOf()函数的作用是返回最适合引用类型的原始值,如果没有原始值,则会返回引用类型自身。<br />**引用类型的默认实现**:
  4. - `**Object**`类型数据的valueOf()函数**默认返回"{}"**,即一个空的对象字面量。
  5. - `**Array**`的valueOf()函数**返回的是数组本身**,例如[1, 2, 3].valueOf()返回的结果为“[1,2,3]”。
  6. - `**function**`的valueOf()函数**返回的是函数本身**,例如(function(x){return x *2;}).valueOf()返回的结果为函数本身“function(x){return x * 2;}”
  7. - `**Date**`的valueOf()函数返回的是**指定日期的时间戳**,例如new Date().valueOf()返回的结果为“1543130166771”
  8. **使用**:<br />一个引用类型的数据在转换为`**Number**`类型时:
  9. - 如果对象具有valueOf()函数,则会优先调用valueOf()函数,如果valueOf()函数返回一个原始值,则会直接将这个原始值转换为数字表示,并返回该数字。
  10. - 如果对象没有valueOf()函数,或者valueOf()函数返回的不是原生数据类型,则再调用toString()函数,如果toString()函数返回的结果是一个原始值,则会将这个结果转换为数字表示,并返回该数字。
  11. - 如果通过toString()函数或者valueOf()函数都无法获得一个原始值,则会直接抛出类型转换异常
  12. ```javascript
  13. var arr = []
  14. arr.toString = function(){
  15. console.log('执行了toString')
  16. return []
  17. }
  18. arr.valueOf = function(){
  19. console.log('执行了valueOf')
  20. return []
  21. }
  22. console.log(Number(arr))
  23. // 执行了valueOf
  24. // 执行了toString
  25. // TypeError: Cannot convert object to primitive value