Undefined类型
Undefined类型只有一个唯一的字面值undefined,表示的是一个变量不存在。
下面是4种常见的出现undefined的场景。
① 使用只声明而未初始化的变量时,会返回“undefined”。
var a;
console.log(a); // undefined
② 获取一个对象的某个不存在的属性(自身属性和原型链继承属性)时,会返回“undefined”。
var obj = {
name: 'xjp'
}
console.log(obj.age) // undefined
③ 函数没有明确的返回值时,却在其他地方使用了返回值,会返回“undefined”。
function test() {}
console.log(test()) // undefined
④ 函数定义时使用了多个形参,而在调用时传递的实参数量少于形参数量,那么未匹配上的参数就为“undefined”。
function test(param1, param2, param3) {
console.log(param3)
}
test(1, 2) // undefined
Null类型
Null类型只有一个唯一的字面值null,表示一个空指针对象,这也是在使用typeof运算符检测null值时会返回“object”的原因。
下面是3种常见的出现null的场景。
① 一般情况下,如果声明的变量是为了以后保存某个值,则在声明时将其赋值为“null”。
var obj = null
function foo() {
return {
name: 'xjp'
}
}
obj = foo()
② JavaScript在获取DOM元素时,如果没有获取到指定的元素对象,就会返回“null”。
document.querySelector('#id') // null
③ 在使用正则表达式进行捕获时,如果没有捕获结果,就会返回“null”。
'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
- **Undefined类型派生自Null类型**,所以在非严格相等的情况下,两者是相等的`null == undefined // true`
**不同点**
- **null是JavaScript中的关键字,而undefined是JavaScript中的一个全局变量(内置属性),即挂载在window对象上的一个变量,并不是关键字(也不能作为 Javascript 的变量或函数名)。**
- 在使用typeof运算符检测时,Undefined类型的值会返回“undefined”,而Null类型的值会返回“object”。
- 在通过call调用toString()函数时,Undefined类型的值会返回“[object Undefined]”,而Null类型的值会返回“[object Null]”。
- 在需要进行字符串类型的转换时,null会转换为字符串"null",而undefined会转换为字符串"undefined"。
- 在需要进行数值类型的转换时,undefined会转换为NaN,无法参与计算;null会转换为0,可以参与计算。
- 无论在什么情况下都没有必要将一个变量显式设置为undefined。如果需要定义某个变量来保存将来要使用的对象,应该将其初始化为null。这样不仅能将null作为空对象指针的惯例,还有助于区分null和undefined。
<a name="oUUVu"></a>
# Boolean类型
Boolean类型(又称布尔类型)的字面值只有两个,分别是true和false,它们是区分大小写的,其他值(如True和False),并不是Boolean类型的值。
Boolean类型使用最多的场景就是用于if语句判断。在JavaScript中,if语句可以接受任何类型的表达式,即if(a)语句中的a,可以是Boolean、Number、String、Object、Function、Null、Undefined中的任何类型。
如果a不是Boolean类型的值,那么JavaScript解释器会自动调用Boolean()函数对a进行类型转换,返回最终符合if语句判断的true或者false值。
<a name="jDMBC"></a>
## 不同类型转换为Boolean类型
- **String类型转换为Boolean类型**
- 空字符串`""`或者`''`都会转换为false。
- 任何非空字符串都会转换为true,包括只有空格的字符串`" "`。
- **Number类型转换为Boolean类型**
- `0`和`NaN`会转换为false。
- 除了0和NaN以外,都会转换为true,包括表示无穷大和无穷小的Infinity和-Infinity。
- **Object类型转换为Boolean类型**
- 当object为null时,会转换为false。
- 如果object不为null,则都会转换为true,包括空对象`{}`。
- **Function类型转换为Boolean类型**
- 任何Function类型的值都会转换为true。
- **Null类型转换为Boolean类型**
- Null类型只有一个null值,会转换为false。
- **Undefined类型转换为Boolean类型**
- Undefined类型只有一个undefined值,会转换为false。
<a name="muUPt"></a>
# Number类型
在JavaScript中,Number类型的数据既包括了**整型数据**,也包括了**浮点型数据**,它们都统一采用64位浮点数进行存储。
<a name="KiuVJ"></a>
## 整型数据
最基本的数值采用的是十进制整数,另外,数值还可以通过八进制或者十六进制表示。<br />**① 八进制。**如果想要用八进制表示一个数值,那么首位必须是0,其他位必须是0~7的八进制序列。如果后面位数的字面值大于7,则破坏了八进制数据表示规则,前面的0会被忽略,当作十进制数据处理。
```javascript
var num1 = 024 // 4+2*8=20 八进制
var num2 = 079 //79 最后一位9超出了八进制字面值,当十进制处理
② 十六进制。如果想要用十六进制表示一个数值,那么前两位必须是0x,其他位必须是十六进制序列(0~9,a~f或者A~F)。如果超过了十六进制序列,则会抛出异常。
var num3 = 0x3f // 15+3*16=63
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表示。
因为浮点型数使用64位存储时,最多只能存储52位的小数位,对于一些存在无限循环的小数位浮点数,会截取前52位,从而丢失精度,所以会出现上面实例中的结果。
计算过程:以0.1 + 0.2 = 0.30000000000000004
的运算为例,首先将各个浮点数的小数位按照“乘2取整,顺序排列”的方法转换成二进制表示。
// 用2乘以浮点数,将积的整数部分取出;然后再用2乘以余下的小数部分,又得到一个积;再将积的整数部分取出,如此推进,直到积中的小数部分为零为止。
0.1*2=0.2 // 取整数部分0
0.2*2=0.4 // 取整数部分0
0.4*2=0.8 // 取整数部分0
0.8*2=1.6 // 取整数部分1
0.6*2=1.2 // 取整数部分1
0.2*2=0.4 // 取整数部分0
0.4*2=0.8 // 取整数部分0
0.8*2=1.6 // 取整数部分1
//···无限循环
因此0.1转换成二进制表示为0.0 0011 0011 0011 0011 0011 0011……(无限循环)。同理对0.2进行二进制的转换结果为0.0011 0011 0011 0011 00110011……(无限循环)。
将0.1与0.2相加,然后转换成52位精度的浮点型表示。
得到的结果为0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 11001100 1100,转换成十进制值为0.30000000000000004。
解决方案:将浮点数先乘以一定的数值(10、100、100·····)转换为整数,通过整数进行运算,然后将结果除以相同的数值转换成浮点数后返回。
const operationObj = {
// 处理传入的参数,不管传入的是数组还是以逗号分隔的参数都处理为数组
handleParams(args) {
return Array.prototype.concat.apply([], args)
},
/** 乘数因子,根据小数位数计算
*1、首先判断有无小数点,无返回1
*2、有小数点,将第一个小数点后的位数长度作为Math.pow()函数的参数进行计算,如2.01乘数因子为100
*/
multiplier(x){
let parts = x.toString().split('.')
return parts.length < 2?1:Math.pow(10, parts[1].length)
},
/** 获取多个数据中最大的乘数因子
*例如1.3乘数因子为10,2.01乘数因子为100,则最大乘数因子为100
*/
selectMaxFactor() {
let args = Array.prototype.slice.call(arguments)
let argArr = this.handleParams(args)
return argArr.reduce((accum, next) => {
let num = this.multiplier(next);
return Math.max(accum, num)
}, 1)
},
// 加法运算
add(...args) {
let calArr = this.handleParams(args);
let maxFactor = this.selectMaxFactor(calArr) // 获取参与运算值的最大乘数因子
let sum = calArr.reduce((accum, next) => {
// 将浮点数乘以最大因子,转成整数参与运算
return accum + Math.round(next*maxFactor)
}, 0)
return sum / maxFactor // 最后再除以最大乘数因子
},
// 减法运算
subtract(...args){
let calArr = this.handleParams(args);
let maxFactor = this.selectMaxFactor(calArr)
let diff = calArr.reduce((accum, next, index) => {
// reduce()函数在未传入初始值时,index从1开始,第一位参与运算的值需要乘最大因子
if(index === 1) {
return Math.round(accum*maxFactor) - Math.round(next*maxFactor)
}
// accum作为上一次运算的结果,不需要再乘最大因子
return Math.round(accum) - Math.round(next*maxFactor)
})
return diff / maxFactor
},
// 乘法运算
multiply(...args){
let calArr = this.handleParams(args);
let maxFactor = this.selectMaxFactor(calArr)
calArr = calArr.map(item => item*maxFactor)
let multi = calArr.reduce((accum, next) => {
return Math.round(accum)*Math.round(next)
}, 1)
return multi / Math.pow(maxFactor, calArr.length)
},
// 除法运算
divide(...args) {
let calArr = this.handleParams(args);
let quotient = calArr.reduce((accum, next) => {
let maxFactor = this.selectMaxFactor(accum, next)
return Math.round(accum*maxFactor)/Math.round(next*maxFactor)
})
return quotient
}
}
console.log(operationObj.add(0.1, 0.7))
console.log(operationObj.subtract(0.4, 0.6))
console.log(operationObj.multiply(0.8, 123))
console.log(operationObj.divide(0.3, 0.1))
Number类型转换
在JavaScript中,一共有3个函数可以将其他类型的值转换为Number类型的,分别是**Number()函数**
、**parseInt()函数**
、**parseFloat()函数**
Number()函数
Number()函数可以用于将任何类型转换为Number类型,它在转换时遵循下列规则。
① 如果是数字,会按照对应的进制数据格式,统一转换为十进制并返回。
Number(10) // 10
Number(010) // 8
Number(0x10) // 16
② Boolean类型转换为Number类型,true将返回为“1”,false将返回为“0”。
Number(true) // 1
Number(false) // 0
③Null类型转换为Number类型,则返回“0”。
Number(null) // 0
④ Undefined类型转换为Number类型,则返回“NaN”。
Number(undefined) // NaN
⑤ String类型转换为Number类型,则遵循下列规则:
- 如果是空字符串或为连续多个空格,则转换为0
- 如果字符串中只包含数字,则会转换成十进制数;如果前面有0,会直接省略掉,例如”0123”会转换为123
- 如果字符串中包含的是有效的浮点数,则同样按照十进制转换,前置的多个重复的0会被清空,只保留一个,例如”00.23”会转换为0.23
- 如果字符串中包含有效的十六进制格式,则会按照十进制转换,例如”0x3f”会转换为63
- 如果字符串是有效的八进制形式,则不会按照八进制转换,而是直接按照十进制转换并输出,因为前置的0会被直接忽略。
如果字符串中包含了除上述格式以外的字符串,则会直接转换为NaN
Number('') // 0
Number(' ') // 0
Number('0123') // 123
Number('00.12') // 0.12
Number('0x21') // 33
Number('010') // 10 八进制直接按照十进制输出
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
<a name="nXnnn"></a>
### parseInt()函数
parseInt()函数用于解析一个字符串,并返回指定的基数对应的**整数值**。如果该字符串无法转换成Number类型,则会返回“NaN”。<br />语法格式:`parseInt(string, radix)`
- 参数`string`表示要被解析的值,如果该参数不是一个字符串,那么会使用toString()函数将其转换成字符串,而字符串前面的空白符会被忽略。
- `radix`表示的是进制转换的基数,数据范围是**2~36**,可以是使用频率比较高的二进制、十进制、八进制和十六进制等,默认值为10。因为对相同的数采用不同进制进行处理时可能会得到不同的结果,所以在任何情况下使用parseInt()函数时,建议都手动补充第二个表示基数的参数。
- 任何整数以0为基数取整时,都会返回本身;不满足基数的整数在处理后会返回“NaN”
1. **非字符串类型转换为字符串类型**
```javascript
parseInt('0x12', 16) // 18 直接将字符串"0x12"转换为十六进制数
parseInt(0x12, 16) // 24 由于传入的是十六进制数,先转成十进制18,再转成字符串"18",再将字符串"18"转换成十六进制数
- 数据截取的前置匹配原则
parseInt()函数在做转换时,对于传入的字符串会采用前置匹配的原则。即从字符串的第一个字符开始匹配,如果处于基数指定的范围,则保留并继续往后匹配满足条件的字符,直到某个字符不满足基数指定的数据范围,则从该字符开始,舍弃后面的全部字符。在获取到满足条件的字符后,将这些字符转换为整数。
parseInt('fg123', 16) // 15
对于字符串'fg123'
,首先从第一个字符开始,'f'
是满足十六进制的数据,因为十六进制数据范围是0~9
,a~f(A~F)
,所以保留'f'
;然后是第二个字符'g'
,它不满足十六进制数据范围,因此从第二个字符至最后一个字符全部舍弃,最终字符串只保留字符'f'
;然后将字符'f'
转换成十六进制的数据,为15,因此最后返回的结果为“15”。
如果遇到的字符串是以”0x”开头的,那么在按照十六进制处理时,会计算后面满足条件的字符串;如果按照十进制(或八进制)处理,则会直接返回“0”。
parseInt('0x12', 16) // 2+1*16=18
parseInt('0x12', 10) // 0
parseInt('0x12', 8) // 0
需要注意的一点是,如果传入的字符串中涉及算术运算,则不执行,算术符号会被当作字符处理;如果传入的参数是算术运算表达式,则会先运算完成得到结果,再参与parseInt()函数的计算。
parseInt('15*3', 10) // 15 直接当字符串处理,不会进行算术运算
parseInt(15*3, 10) // 45 先运算得到45,再进行parseInt(45, 10)的运算
- 对包含字符e的不同数据的处理差异
当传入的参数本身就是Number类型时,会将e按照科学计数法计算后转换成字符串,然后按照对应的基数转换得到最终的结果。
如果传入的字符串中直接包含e,那么并不会按照科学计数法处理,而是会判断字符e是否处在可处理的进制范围内,如果不在则直接忽略,如果在则转换成对应的进制数。
parseInt(6e3, 10) // 6*10的三次方=6000
parseInt(6e3, 16) // 24576 实际执行parseInt('6000', 16)
parseInt('6e3', 10) // 6 字符'e'不在十进制所能表达的范围内,所以会直接省略
parseInt('6e3', 16) // 3+14*16+6*16*16=1763
- 对浮点型数的处理
如果传入的值是浮点型数,则会忽略小数点及后面的数,直接取整。
parseInt('6.01', 10) // 6
parseInt('6.99', 16) //6
练习:
parseInt('0xF', 16)
parseInt('F', 16)
parseInt('fxx123', 16)
parseInt('12', 13)
parseInt('015', 10)
parseInt(15.99, 10)
parseInt('15,123', 10)
parseInt('15*2', 10)
parseInt('15e2', 10)
parseInt('15px', 10)
parseInt('17', 8)
parseInt(021, 8)
parseInt('1111', 2)
- map()函数与parseInt()函数的隐形坑
情景:将一个数组[‘1’,’2’, ‘3’, ‘4’],全部转换为整数,调用Array的map()函数中调用parseInt()函数
var arr = ['1', '2', '3', '4']
var result = arr.map(parseInt)
console.log(result)
结果:得到的结果是[1, NaN, NaN, NaN],与我们期望的结果[1, 2, 3, 4]差别很大,这是为什么呢?
解析:
// arr.map(parseInt)等效于
arr.map(function(val, index){
return parseInt(val, index)
})
// parseInt()函数接收的第二个参数实际为数组的索引值,所以实际执行的是
parseInt('1', 0) // 1 任何整数以0为基数取整时,都会返回本身
parseInt('2', 1) // NaN parseInt对应的基数只能为2~36,不满足基数的整数在处理后会返回“NaN”
parseInt('3', 2) // NaN 二进制时只有0和1,3超出了二进制的表示范围
parseInt('4', 3) // NaN 同上
解决:map()函数中使用parseInt()函数时需要注意不能直接将parseInt()函数作为map()函数的参数,而是需要在map()函数的回调函数中使用,并尽量指定基数
var arr = ['1', '2', '3', '4']
var result = arr.map(item => parseInt(item, 10))
console.log(result) // [1,2,3,4]
parseFloat()函数
parseFloat()
函数用于解析一个字符串,返回对应的浮点数。如果给定值不能转换为数值,则会返回“NaN”
。
与parseInt()
函数相比,parseFloat()
函数没有进制的概念,只解析十进制,因此不能指定底数。
① 如果在解析过程中遇到了正负号(+ / -)、数字0~9、小数点或者科学计数法(e / E)以外的字符,则会忽略从该字符开始至结束的所有字符,然后返回当前已经解析的字符的浮点数形式。其中,正负号必须出现在字符的第一位,而且不能连续出现。
parseFloat('+1.2') // 1.2
parseFloat('-1.2') // -1.2
parseFloat('++1.2') // NaN 符号不能连续出现
parseFloat('--1.2') // NaN 符号不能连续出现
parseFloat('1+1.2') // 1 +不是出现在第一位,不会当符号位处理,直接舍弃及之后
② 字符串前面的空白符会直接忽略,如果第一个字符就无法解析,则会直接返回“NaN”。
parseFloat(' 1.2') // 1.2
parseFloat('f1.2') // NaN
③ 对于字符串中出现的合法科学运算符e,会先进行运算处理然后转换成浮点型数,这点与parseInt()函数的处理有很大的不同。
parseFloat('4e3') // 4000 先进行科学计数法运算
parseInt('4e3', 10) // 4 不进行运算,直接从第一个字符开始匹配
④ 对于小数点,只能正确匹配第一个,第二个小数点是无效的,它后面的字符也都将被忽略。
parseFloat('11.20') // 11.2
parseFloat('11.2.1') // 11.2
练习:
parseFloat('123AF')
parseFloat('0xa')
parseFloat('22.5')
parseFloat('00.3.56')
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产生的条件:
- 一方面是在数据运算时,返回了一个无法表示的数值,例如0 / 0就会返回“NaN”。有一点需要注意的是除了0 / 0,其他数据除以0都返回“Infinity”。
- 另一方面是在需要做强制类型转换时,某些数据不能直接转换为数值类型,就会返回“NaN”,例如1 - ‘a’ = NaN,因为字符串’a’无法参与数值运算。
isNaN()函数
作用:它用来确定一个任意类型变量是不是NaN。NaN是一个Number类型的数值,只不过这个值无法用真实的数字表示。
问题:如果传递的参数是Number类型数据,可以很容易判断是不是NaN。如果传递的参数是非Number类型,它返回的结果往往会让人费解:isNaN({}) // true
空对象{}
明明不是一个NaN的数据,应该返回的是“false”,为什么会返回“true”呢?
执行:isNaN()函数会先对传入的变量进行类型转换,去判断值能否转换为数字,如果能转换成数字则会返回“false”,如果无法转换则会返回“true”。isNaN(NaN) // true 任何涉及NaN的操作都会返回“NaN”
isNaN(undefined) // true Number(undefined)转换成数字为NaN
isNaN({}) // true
isNaN(true) // false Number(true)转换成数字为1
isNaN(null) // false Number(null)转换成数字为0
isNaN(1) // false
isNaN('') // false Number('')转换成数值为0
isNaN(' ') // false 同上
isNaN('1') // false Number('1')转换成数字1
isNaN('js') // true
isNaN(new Date()) // false Number(new Date())会转换为对应的时间戳如1623223946176
isNaN(new Date().toString()) // true 字符串表示的时间,不是字符串表示的时间戳,无法转换
Number.isNaN()函数
isNaN()
函数本身存在误导性,而ES6中的Number.isNaN()
函数会在真正意义上去判断变量是否为NaN,不会做数据类型转换。只有在传入的值为NaN时,才会返回“true”,传入其他任何类型的值时会返回“false”。
兼容处理:在非ES6环境中使用ES6中的isNaN()函数Number.isNaN(NaN) // true
Number.isNaN(0/0) // true
Number.isNaN(undefined) // false
Number.isNaN(null) // false
Number.isNaN(true) // false
Number.isNaN('') // false
Number.isNaN(123) // false
因为在所有类型的数据中,如果一个变量和自身作比较,只有在变量值为NaN时才会返回“false”,其他情况都是返回“true”。if(!Number.isNaN) {
Number.isNaN = function(n) {
return n!==n // 只有在n为NaN的时候才返回“true”,
}
}
总结
isNaN()函数与Number.isNaN()函数的差别主要是对传入的值是否进行数据类型转换:
isNaN()
函数在判断是否为NaN时,需要先进行数据类型转换,只有在无法转换为数字时才会返回“true”;Number.isNaN()
函数在判断是否为NaN时,只需要判断传入的值是否为NaN,并不会进行数据类型转换。String类型
JavaScript中的String类型(字符串类型)既可以通过双引号””表示,也可以通过单引号’’表示,而且是完全等效的,这点与Java、PHP等语言在字符串的处理上是不同的。
在JavaScript中,有3种定义字符串的方式,分别是字符串字面量,直接调用String()函数与new String()构造函数。
字符串字面量
含义:字符串字面量就是直接通过单引号或者双引号定义字符串的方式。只不过使用单引号开头的字符串就要使用单引号结尾,使用双引号开头的字符串就要使用双引号结尾。
var str = 'xjp' // 正确写法
var str = "xjp" // 正确写法
var str = 'xjp" // 错误写法,首尾符号不一样
直接调用String()函数
直接调用String()函数,会将传入的任何类型的值转换成字符串类型,在转换时遵循的规则如下。
① 如果是基本数据类型,则会直接将字面值转换为字符串表示形式。
String(123) // '123'
String(123.12) // '123.12'
String(true) // 'true'
String(false) // 'false'
String(null) // 'null'
String(undefined) // 'undefined'
String('this is a string') // 'this is a string' 如果值为字符串,则直接返回字符串本身
②如果值为引用类型,则会先调用**toString()**
函数获取返回值,如果返回值是基本数据类型则转换字符串类型,如果不是,则再调用对象的**valueOf()**
函数获取返回值,如果返回值是基本数据类型则转换字符串类型,如果还满足,则会抛出类型转换的异常。
// 通过toString()函数将对象正确转换成String类型的示例
var obj = {
age: 21,
valueOf:function(){
return this.age;
},
toString:funvtion(){
return 'good';
}
}
String(obj) // 'good'
// 通过valueOf()函数将对象正确转换成String类型的示例
var obj2 = {
age: '21',
valueOf:function(){
return this.age;
},
toString:function(){
return [];
}
}
String(obj2) // '21'
// 如果toString()和valueOf()返回的都是对象类型而无法转换成原生类型时,则会抛出类型转换的异常
var obj3 = {
age: '21',
valueOf:function(){
return [];
},
toString:function(){
return [];
}
}
String(obj3) // TypeError: Cannot convert object to primitive value
更简单的转换方法:直接使用加号(+)拼接一个空字符串(””)。
console.log(123 + '') // '123'
console.log([1, 2, 3] + '') // '1,2,3'
console.log(true + '') // 'true'
new String()构造函数
**new String()**
构造函数使用new运算符生成String类型的实例,对于传入的参数同样采用和上述String()
函数一样的类型转换策略,最后的返回值是一个String类型对象的实例。
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’
<a name="fNlYd"></a>
## 函数的调用
`**eval()函数**`会将基本字符串作为源代码处理,如果涉及表达式会直接进行运算,返回运算后的结果;而字符串对象则会被看作对象处理,返回对象本身。
```javascript
var s1 = '2 + 2' // 创建一个字符串字面量
var s2 = new String('2 + 2') // 创建一个对象字符串
console.log(eval(s1)) // 4
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
<a name="pd1TH"></a>
## valueOf()函数
**作用**:valueOf()函数的作用是返回最适合引用类型的原始值,如果没有原始值,则会返回引用类型自身。<br />**引用类型的默认实现**:
- `**Object**`类型数据的valueOf()函数**默认返回"{}"**,即一个空的对象字面量。
- `**Array**`的valueOf()函数**返回的是数组本身**,例如[1, 2, 3].valueOf()返回的结果为“[1,2,3]”。
- `**function**`的valueOf()函数**返回的是函数本身**,例如(function(x){return x *2;}).valueOf()返回的结果为函数本身“function(x){return x * 2;}”
- `**Date**`的valueOf()函数返回的是**指定日期的时间戳**,例如new Date().valueOf()返回的结果为“1543130166771”
**使用**:<br />一个引用类型的数据在转换为`**Number**`类型时:
- 如果对象具有valueOf()函数,则会优先调用valueOf()函数,如果valueOf()函数返回一个原始值,则会直接将这个原始值转换为数字表示,并返回该数字。
- 如果对象没有valueOf()函数,或者valueOf()函数返回的不是原生数据类型,则再调用toString()函数,如果toString()函数返回的结果是一个原始值,则会将这个结果转换为数字表示,并返回该数字。
- 如果通过toString()函数或者valueOf()函数都无法获得一个原始值,则会直接抛出类型转换异常
```javascript
var arr = []
arr.toString = function(){
console.log('执行了toString')
return []
}
arr.valueOf = function(){
console.log('执行了valueOf')
return []
}
console.log(Number(arr))
// 执行了valueOf
// 执行了toString
// TypeError: Cannot convert object to primitive value