数据类型分类
原始数据类型
- number: NaN【不是一个有效数字】、Infinity【无穷大的值】,1,11
- string: 基于 单引号/双引号/反引号` 包起来的都是字符串
- boolean: true/false
- null
- undefined
- symbol: 唯一值
- bigint:大数
对象类型
- 标准普通对象 object,如{num:100}
- 标准特殊对象 Array/RegExp/Date/Error/Math/ArrayBuffer/DataView/Set/Map...,如[10,20]
- 非标准特殊对象 Number/String/Boolean/Symbol/Bigint...【基于构造函数或Object创造出来的原始值对象类型的格式信息,类型属于对象类型】
- 可调用对象【实现了call方法】 function,如fn(){}
要点:
NaN
- NaN === NaN//false 不能基于“是否等于NaN”来检测值是否为有效数字
- isNaN([value]) 不论[value]是什么类型,默认隐式转换为数字类型Number([value]),再校验是否为有效数字,如果是有效数字,返回false,不是有效数字返回true
Object.is(NaN,NaN):true【不兼容IE,Edge除外】
Object.is不会隐式转换
Object.is('123',123)//false
Object.is(NaN,'123a')//false
isNaN('123a')//true
Object.is(10,20)//false
Symbol
console.log(Symbol() === Symbol()); //false 创建了两个唯一值
console.log(Symbol('AA') === Symbol('AA')); //false
适用场景
- 对象的唯一属性
给对象设置唯一的属性「对象的属性名类型:字符串 & Symbol类型」
let key = Symbol()
let obj = {
[key]: 100 //这里必须使用[]
}
console.log(obj[key])//100
let arr = Object.getOwnPropertySymbols(obj)//获取当前对象所有Symbol类型的私有属性,输出结果是数组
arr.forEach(item => {
console.log(obj[item])//100
})
对象作为属性,会被覆盖
let a = {
name: 'zhufeng'
};
let b = {
name: 'web'
};
let obj = {};
obj[a] = 100; //obj["[object Object]"]=100
obj[b] = 200; //obj["[object Object]"]=200
console.log(obj[a]); //200
- 宏观管理标识
保证标志唯一性
redux/vuex公共状态管理的时候,派发的行为标识就可以基于Symbol类型进行宏管理
- Symbol.hasInstance\Symbol.toStringTag\Symbol.toPrimitive\Symbol.iterator...很多JS底层的处理机制,就是基于这些属性方法实现的
Symbol.hasInstance
Symbol.iterator
Symbol.toPrimitive
Symbol.toStringTag
...
Symbol.toPrimitive应用
原理
xxx[Symbol.toPrimitive](hint){
// hint:'number' / 'string' / 'default'
// + number:获取当前对象的数字类型的原始值
// + string:获取当前对象的字符串类型的原始值
// + default:根据操作获取数字或者字符串类型的原始值
}
重写
let obj = {
name: 'zhufeng',
age: 12,
[Symbol.toPrimitive](hint) {
let result;
switch (hint) {
case 'number':
result = 0;
break;
case 'string':
result = JSON.stringify(obj);
break;
default:
result = "";
}
return result;
}
};
console.log(Number(obj)); // hint:"number"
console.log(String(obj)); // hint:"string"
console.log(10 + obj); // hint:"default"
console.log(10 - obj); // hint:"number"
bigint
数字后面加个n就是大数类型
- 10 Number类型
- 10n BigInt类型
若超过最大安全数或小于最小安全数,就会出现计算不准确,受限于浏览器的计算精准度
console.log(Number.MAX_VALUE)//1.7976931348623157e+308
console.log(Number.MAX_SAFE_INTEGER)//9007199254740991
console.log(Number.MIN_VALUE)//5e-324
console.log(Number.MIN_SAFE_INTEGER)//-90071992zuida54740991
问题:服务器中有longInt长整型这种值,如果把这样的值返回给客户端,则客户端无法进行有效的处理,一般服务器都是以字符串返回,但是字符串进行计算还是需要转换为数字才可以,还是不准确
可以使用BigInt处理,把服务器返回的值变为bigint格式的,然后进行运算「保证了运算的准确性{进行运算的另外一个值也应该是bigint类型的}」;把运算的结果,再次变为字符串,发送给服务器即可!!
console.log((BigInt('9007199254740992134') - BigInt('1')).toString())//'9007199254740992133'
面试题:
- 精确计算0.1+0.2
(0.1+0.2)*10/10 //0.3
- 精确计算0.1+0.0000002
parseInt((BigInt('100000')+BigInt('2')).toString())/1000000 //0.100002
字面量:原始值
let n = 10;
构造函数:对象值
let m = new Number(10);
typeof n //'number'
typeof m //'object'
注意Symbol和BigInt不能通过这种方式
new Symbol() //Uncaught TypeError: Symbol is not a constructor
new BigInt() //Uncaught TypeError: BigInt is not a constructor
但是:
let c = new Object(Symbol(10))
typeof c //"object"
let d = new Object(BigInt(10))
typeof d //"object"
数据类型转换规则
其它类型 【原始值】 转换为 对象:Object([value])
其它类型 转换为 数字
Number([value])
- 一般用于隐式转换[数学运算、isNaN、==比较...];
- 用于字符串转换为数字,空字符串会变为0,字符串只要出现非有效字符串结果就是NaN
- 布尔转换为数字 true 1 false 0
- null 转换为 0
- undefined转换为 NaN
- 转换Symbol会报错
- 转换BigInt 就会 正常转换
- 对象转换遵循 Symbol.toPrimitive/valueOf/toString/Number
Number('')//0
Number('ab')//NaN
Number('10n')//NaN
Number(true)//1
Number(null)//0
Number(undefined)//NaN
Number(Symbol())//报错Uncaught TypeError: Cannot convert a Symbol value to a number
Number(10n)//10
parseInt/parseFloat([value])
- 首先会把 [value] 变为字符串,从字符串左侧第一个字符开始查找,查到找到一个非有效数字字符为止,把找到的结果转换为数字,一个都没找到,结果就是NaN ,而parseFloat会多识别一个小数点
parseInt('12.ad')//12
parseFloat('12.ad')//12
parseFloat('12.01ad')//12.01
parseFloat('ab12.01ad')//NaN
parseInt('null')//NaN
其它类型 转换为 字符串
原始值转换
直接用引号包起来【bigint会去除n】;
'12'//'12'
(10n).toString()//'10'
(Symbol()).toString()//"Symbol()"
把对象obj转换为字符串
两种方式:
- String(obj):Symbol.toPrimitive -> valueOf -> toString 浏览器默认隐式转换用的是String(obj)
- obj.toString() :直接调用这个方法转字符串,不会在执行以上的规则
除对象转换为字符串是比较特殊的,其它都是下面的情况:
- 使用toString() 可以转换为字符串【 排除Object.prototype.toString{检测数据类型}】
- 字符串/模板字符串拼接 【"+"在JS中除了数学运算,还有字符串拼接功能,但是其它运算符一般都是数学运算】
带“+”表达式的情况分析
CASE1:“+”只有一边,直接转换成数字
基于:Number(xxx)
let n = '10'
+n //10 转换为数字
++n //11 转换为数字然后累加1
n++ //11 转换为数字然后累加1
面试题:i++ 和i=i+1以及i+=1 三个是否一样?
不一样,i=i+1和i+=1是一样的
i++一定返回的是数字,但是i=i+1不一定返回的是数字,有可能是字符串拼接
CASE2:“+”有两边,有一边出现了字符串或者部分对象
按照字符串拼接处理的,对象基于String(xxx),其他基于toString()
1+'1' //'11'
'number'+[];//'number'+[].toString()=>'number'+""=>'number'
'number'+{};//'number'+String({})=>'number'+"[object Object]"=>'number[object Object]'
CASE3:“+”有两边,有一边是对象
遵循规则:
@1 调用objSymbol.toPrimitive
@2 没有这个属性,则再次调用valueOf
@3 valueOf获取的不是原始值,则继续toString,此时获取的结果是字符串,“+”就变为字符串拼接了
let n = "10"
{}+10 //10 把左侧当成代码块,,不参与运算,运算只有+n
10+{} //先调用{}的toString方法,转为字符串"[object Object]",再进行字符串拼接,所以就是"10[object Object]" 字符串拼接
[]+10 //10
10+[]//10
console.log(10 + [10]);
// 没有Symbol.toPrimitive -> valueOf获取的也不是原始值 -> 调用toString "10" => "1010"
console.log(10 + {});
// 没有Symbol.toPrimitive -> valueOf获取的也不是原始值 -> 调用toString "[object Object]" => "10[object Object]"
console.log(10 + new Date());
// 调用日期的Symbol.toPrimitive('default') => "10Sun Jul 25 2021 11:28:37 GMT+0800 (中国标准时间)"
console.log(10 + new Number(10));
// 没有Symbol.toPrimitive -> valueOf 10 => 20
console.log(10 + new String('10'));
// 没有Symbol.toPrimitive -> valueOf "10" => "1010"
let obj = { x: 10 }
console.log(10 + obj) //"10[object Object]"
//若要得到20的结果,怎么做呢?
//利用上述规则改造
let obj = {
x: 10,
[Symbol.toPrimitive](hint) {//hint取值:default、string、number。推荐使用hint
return this.x
}
}
console.log(10 + obj) //20
CASE4: “+”有两边, 剩下的情况一般都是数学运算
1+2//3
100+true;
//100+Number(true)=>101
100+undefined
//100+Number(undefined)=>100+NaN=>NaN
其它类型 转换为 布尔类型
规则
只有"0、NaN、null、undefined、空字符串"会变为false,其余都是转换为true
转换方式(五种)
- Boolean([value])
- !![value]
- ![value] 转换为布尔类型取反
- 条件判断 例如: if(1){}
- A||B A&&B
!![]//true
!!-1//true
JS中验证两个值是否相等
- ==:相等【若两边类型不一样,首先会隐式转换为相同类型,然后再做比较】
-
- 对象==字符串 对象会转成字符串
- null==undefined //true 【===不相等】,但是null/undefined和其它任何值都不会相等
- NaN==NaN //false
- Symbol()==Symbol( ) //false
- 其它情况【例如:对象==数字、字符串==布尔...】都是转换为数字,再进行比较的
- === : 绝对相等 【要求两边类型和值相等,例如:switch case】
- Object.is([val], [val])
- ...
Object.is(NaN, NaN)//true NaN本身并不相等
NaN === NaN //false
NaN == NaN //false
Object.is('10', 10)//false
null == ''//false
null == 0//false
undefined == ''//false
undefined == 0//false
isNaN(NaN)//true
'' == false//转换成0 == 0 ,true
''==0;//转换成0 == 0 ,true
题目:
- 下面两个答案是
![]==false //true []转成true,![]就是false
[]==false //true 属于对象==布尔比较,[]遵循对象转换为数字规则,最终[].toString()得到'',
最后Number('')即为0,false转换为数字就是0,最终[]==false 为true
- a等于多少,才能输出'OK'?
方案1:重写Symbol.toPrimitive
掌握数据类型转换规则,若a是一个对象,我们就可以利用“对象->数字”的规则去做一些处理
利用 == 比较的时候,会把对象转换为数字 Number(a)
- Symbol.toPrimitive
- valueOf
- toString
- 把字符串变为数字
var a = {
i:0,
[Symbol.toPrimitive](){
return ++this.i
}
}
方案2:重写toString
var a = [1, 2, 3];
a.toString = a.shift;
if (a == 1 && a == 2 && a == 3) {
console.log('OK');
}
方案3:数据劫持
在全局上下文中,基于 var/function 声明的变量,并不是给VO(G)设置的全局变量「基于let/const声明的变量才是」,而是给GO(window)全局对象设置的属性 var a=? ==> window.a=?
let i=0;
Object.defineProperty(window, 'a', {
get(){
return ++i;
}
})
面试题
let result = 100+true+21.2+null+undefined+'Tencent'+[]+null+9+false;
console.log(result) //'NaNTencent9null9false'
//100+true 加号两边没有对象,true会使用Number(true)隐式转换成数字1,结果:101
//101+21.2+null 加号两边没有对象,null会使用Number(null)隐式转换成数字0,结果:122.2
//122.2+undefined 加号两边没有对象,undefined会使用Number(undefined)隐式转换成NaN,结果:NaN
//NaN+'Tencent' 加号有字符串,NaN会隐式转换为字符串'NaN',结果:'NaNTencent'
//'NaNTencent'+[]+null+9+false 由于出现了字符串,所以后面都转成字符串拼接,[]调用toString方法转成空字符串'', 结果:'NaNTencent9null9false'
parseInt剖析
parseInt([string],[radix]) [radix]是进制,取值范围2-36,默认值10进制,但是若字符串是以“0x”开头,默认16进制,若radix值是0,和radix等于10效果一致.
- 把[string]看做[radix]进制从左侧找到所有符合这个进制的字符,遇到不符合的结束查找,把找到的字符转换为【10进制】数字
- 若radix<2或radix>36,则结果为NaN
注:浏览器看到0123会自动转成83,所以parseInt(0123)实际上是parseInt(83),则结果就是83
let arr = [27.2, 0, '0013', '14px', 123]
arr = arr.map(parseInt) //重点:还原map完整写法
console.log(arr)
==================================
arr = arr.map((item,index)=>{
//数组有多少项遍历多少次,回掉函数执行多少次
//item->当前项 index->当前项索引
//回调函数的返回值会替换数组中当前项的结果【原始数组不变,以新数组形式返回】
return 'xxx'
})