一、JavaScript中的数据类型
- 简单数据类型(原始类型): String Number Boolean undefined null Symbol(ES6新增,用于表示独一无二的. 值, 因为是原始类型,创建时不需new)
- 引用数据类型(复杂数据类型): Object(Object、Array、Function本质上都是Object)
object ====> 对象转换成原始值,必然会调用toPrimitive()内部函数
二 、隐式转换
当运算符两端的数据类型不一致,会触发隐式转换
1.涉及隐式转换最多的运算符 + 、>和 ==。
1.+运算符即可数字相加,也可以字符串相加。
2.== 不同于===,故也存在隐式转换。
3.- * / 这些运算符只会针对number类型,故转换的结果只能是转换成number类型
2.三种隐式转换类型
2.1隐式转换中将值转为原始值,ToPrimitive:
ToPrimitive介绍:
ToPrimitive有着这样的形式:ToPrimitive(input, PreferredType)
(input是要转换的值,PreferredType是可选参数,仅可以是Number或String类型 )
let obj = {
valueOf: () => 5,
toString: () => 'five'
};
let primitiveValue1 = Number(obj);
console.log(primitiveValue1); // 5
let primitiveValue1 = String(obj);
console.log(primitiveValue1); // five
注意:
(1). PreferredType的值会按照这样的规则来自动设置:
1、该对象为Date类型,则PreferredType被设置为String
2、否则,PreferredType被设置为Number
(2).为什么?
当PreferredType为number时,会先调用valueof方法,若不是原始值,则调用tostring方法,
这样做的好处就是最大可能的保证将传入的值保持为原有值,而PreferredType设置为string,
则先调用tostring一股脑的将传入转为string。
但是当日期为date格式时,valueof方法会取到日期的时间戳,我们获取时间戳那么大一个数字显然没太大意义,
而tostring获取的是日期格式,所以input为date时,设置PreferredType为string更好
(3).ToPrimitive是如何对输入进行类型转化的呢?
(3.1)首先如果PreferredType传为Number:
1、如果输入的值已经是一个原始值,则直接返回它
2、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
3、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
4、否则,抛出TypeError异常。
(3.2)如果PreferredType传为String:
1、如果输入的值已经是一个原始值,则直接返回它
2、否则,调用这个对象的toString()方法,如果toString()方法返回的是一个原始值,则返回这个原始值。
3、否则,如果输入的值是一个对象,则调用该对象的valueOf()方法,
如果valueOf()方法的返回值是一个原始值,则返回这个原始值。
4、否则,抛出TypeError异常。
(3.3)如果不传:
PreferredType会被默认设置为Number,但是当输入input为Date类型,则PreferredType被设置为String
例子:
1. {} + {} // ??? NaN
* 首先是js内部使用ToPrimitive,由于我们没有设置PreferredType,于是PreferredType就是默认值number。
* 所以会执行valueOf方法,({}).valueOf返回的还是{},不是原始值。
* 执行toString()方法,({}).toString返回的是"[object Object]"字符串,是原始值
* 所以结果为"[object Object]" + "[object Object]" = "[object Object][object Object]",
* 为什么不是"[object Object][object Object]" ?
在node中会将以“{”开始,“}”结束的语句外面包裹一层( ),就变成了({ } + { }),结果就符合预期。而普通版本的chrome依然会解析成{};+{},结果就变成了NaN
2.[] + [] // ???
* 两个都是Array对象,不是Date对象,所以PreferredType为Number,
* 所以先调用valueOf(),结果还是[ ],不是原始值,
* 所以继续调用toString(),结果是“ ”原始值,将“ ”返回。
* 第二个[ ]过程是相同的,返回“ ”。加号两边结果都是String类型,所以进行字符串拼接,结果是“ ”
3. {} + [] // ???
* {} ===> "[object Object]" ,然而 [] ===> ' '
* 加号两边结果都是String类型,所以进行字符串拼接,结果是“[object Object]”
* 显示的答案是0! 这是什么原因呢?原来{ } + [ ]被解析成了{ };+[ ],前面是一个空代码块被略过,
剩下+[ ]就成了一元运算。[ ]的原值是” ”, 将” ”转化成Number结果是0
4. [1,2,3] == '1,2,3'
左边数组对象进行隐式转换ToPrimitive([1,2,3]) => '1,2,3'
2.2 通过ToNumber将值转换为数字
2.3 通过ToString将值转换为字符串
3.具体应用
3.1. 字符串连接符 与 算术运算符 隐式转换
规则:字符串连接符 + 其他数据类型 调用String()方法,进行拼接
算术运算符 + 其他数据类型 Number()方法,进行加法计算
1. console.log(1 + 'true')
console.log(undefined+"1")
console.log([1,2,3]+"测试") //1,2,3测试
console.log({name:"田本初"}+"测试") // [object Object]测试
2. console.log(1 + true )
3. console.log(1 + undefined)
4. console.log(1 + null)
结果:
1)String(1) + 'true' = '1true' // 字符串连接符 + 其他数据类型 调用String()方法,进行拼接
2)Number(true) + 1 = 2 // 算术运算符 + 其他数据类型 Number()方法,进行加法计算
3)Number(undefined) + 1 = NaN
4) Number(null) + 1 = 0 + 1 = 1
3.2. 逻辑非隐式转换 与 关系运算符 隐式转换
规则:字符串 和数字比较,字符串转成数字
字符串比较,看CSII码;若为不等长字母,比较同位置字母大小,直至做出判断
1.console.log("2" > 10)
2.console.log("2" > "10")
3.console.log("abc" > "b")
4.console.log("abc" > "aad")
结果:
1.Number('2')=2 > 10 ? false
2.'2'.charCodeAt() = 50 '10'.charCodeAt() = 49 ? true
3.'a' 'b' charCodeAt(). ? false
4.'ab' 'aa' ? true
3.3. 复杂数据类型 在进行 关系运算符 作比较的时候,会隐式调用 valueOf这个方法
规则: 引用类型,先调用valueOf()
var a = {
valueOf: function() {
console.log(123)
return 4
}
}
if(a == 4) {
console.log('haha ')
}
/// 123 hahaha
var a = {
i: 0,
valueOf: function() {
console.log('123', a)
return ++a.i
}
}
if(a == 1 && a == 2 && a == 3) {
console.log('神奇的123 ')
}
/// 神奇的123
4.==运算符
比较操作符 == ,会进行类型转换后再进行比较 , 分析(六种基本情况+三种特殊情况)
4.1.对象和字符串比较
转换规则: 调用ToPrimitive()内部函数,将对象或者数组转换成字符串,然后进行比较
// 数组和字符串
console.log([1, 2, 3] == '1,2,3')
console.log('对象和字符串', [11] == '11')
console.log([] == 0)
// 对象和字符串
let obj = { a: 1}
console.log( obj == '[object Object]')
4.2. 对象和数值比较
转换规则:
调用ToPrimitive()内部函数,将对象转换成字符串;
再调用ToNumber()将字符串转成数字,进行比较
// 对象和数值
console.log('对象和数值', [11] == 11)
分析:ToPrimitive([11]) => '11',ToPrimitive('11') => 11
4.3 对象和布尔值比较
转换规则:对象:调用ToPrimitive()内部函数,将对象转换成字符串;再调用ToNumber()将字符串转成数字;
布尔值:调用ToNumber()将布尔值转成数字;进行比较
console.log('对象和布尔值', [] == false)
分析:ToPrimitive([]) => '',ToPrimitive('') => 0;ToPrimitive('false') => 0
4.4 字符串和数值
转换规则:调用ToNumber()将字符串转换成数字,然后比较
console.log('11' == 11) // true
4.5. 字符串和布尔值
转换规则:调用ToNumber()将字符串转换成数字;调用ToNumber()将布尔值转成数字;然后比较
console.log('1' == true) // true
4.6. 布尔值和数值
转换规则: 调用ToNumber()将布尔值转成数字; 然后比较
console.log(1 == true)
//分析: ToPrimitive('true') => 1
4.7. 存在!运算符(特殊情况1)
转换规则:
先将数据转换成布尔值,其余按照上述规则进行;除了 0、NaN、''、null、undefined转换成false,其余true
1. console.log([] == 0)
console.log([] == false) // true
console.log(![] == false) // true
2. console.log(![] == 0)
3. console.log(![] == [])
结果:1.// true
空数组 `[]` 会被转换为 `0`,因为在比较数组和数字时,JavaScript 会将数组转换为其长度。
2.// true
`!` 是逻辑非操作符,用于取反一个值。空数组 `[]` 在布尔上下文中会被视为 `true`,因为它是一个
存在的对象。 所以 `![]` 的结果是 `false`。
接着,`false` 会被转换为数字 `0`,因此表达式变成了 `false == 0`
3.// false
`![]` 的结果是 `false`。 空数组 `[]` 在布尔上下文中也会被视为 `true`,
所以 `[]` 转换为布尔值后也是 `true`。
4.8.null 和 undefined 的比较(特殊情况二)
转换规则:null 和 undefined都代表着无效的值,但两者数据类型不一样,故不全等
console.log(undefined == undefined) true
console.log(undefined == null) true
console.log(null == null) true
4.9.存在NaN(特殊情况三)
转换规则:NaN(Not a Number)表示不是一个数字,因此 NaN 与任何值都不相等,包括 NaN 本身
console.log(NaN === NaN) false
具体总结
比较运算 x==y, 其中 x 和 y 是值,返回 true 或者 false。这样的比较按如下方式进行:
1、若 Type(x) 与 Type(y) 相同, 则
1* 若 Type(x) 为 Undefined, 返回 true。---
2* 若 Type(x) 为 Null, 返回 true。 ---
3 若 Type(x) 为 Number, 则
(1)、*若 x 为 NaN, 返回 false。
(2)、*若 y 为 NaN, 返回 false。
(3)、若 x 与 y 为相等数值, 返回 true。
(4)、若 x 为 +0 且 y 为 −0, 返回 true。
(5)、若 x 为 −0 且 y 为 +0, 返回 true。
(6)、返回 false。
4 若 Type(x) 为 String, 则当 x 和 y 为完全相同的字符序列(长度相等且相同字符在相同位置)时返回 true。 否则, 返回 false。
5 若 Type(x) 为 Boolean, 当 x 和 y 为同为 true 或者同为 false 时返回 true。 否则, 返回 false。
6* 当 x 和 y 为引用同一对象时返回 true。否则,返回 false。
2、若 x 为 null 且 y 为 undefined, 返回 true。---
3、若 x 为 undefined 且 y 为 null, 返回 true。---
4、*若 Type(x) 为 Number 且 Type(y) 为 String,返回比较 x == ToNumber(y) 的结果。
5、*若 Type(x) 为 String 且 Type(y) 为 Number,返回比较 ToNumber(x) == y 的结果。
6、*若 Type(x) 为 Boolean, 返回比较 ToNumber(x) == y 的结果。
7、*若 Type(y) 为 Boolean, 返回比较 x == ToNumber(y) 的结果。
8、若 Type(x) 为 String 或 Number,且 Type(y) 为 Object,返回比较 x == ToPrimitive(y) 的结果。
9、若 Type(x) 为 Object 且 Type(y) 为 String 或 Number, 返回比较 ToPrimitive(x) == y 的结果。
10、返回 false。
补充:---:null 和 undefined都代表着无效的值
-NaN(Not a Number)表示不是一个数字,因此 NaN 与任何值都不相等,包括 NaN 本身