数据类型
原始数据类型
-
number 数字
-
string 字符串
-
boolean 布尔值
-
null 空对象指针
-
undefined 未定义
-
symbol 唯一值
-
bigint 大数
-
补充说明
- bigint
- 作用:当数值大于最大安全数或者小于最小安全数时,数值的访问及操作都会不准确,可以通过bigint操作。
- 使用场景:
- 当数据库中存储了大于安全数的值并返回给前端时,前端无法操作及访问。
- 解决方案:
- 后端先将该值转化为字符串再传给前端
- 前端再通过bigint做相应的逻辑处理
- 前端再将bigint转换为字符串传递给后端即可
- code
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991 // 如果值为 9007199254740992 > max let num = BigInt('9007199254740992') + BigInt('1') return num.toString(); // 9007199254740993
- symbol
- 作用:创建唯一值
- 使用场景:
- 给对象设置'唯一值'属性名
- 默认情况下,对象属性值均为字符串
- 通过symbol可以创建非字符串的属性名
- 给Map数据类型作属性名使用
- JS用于内置的API实现
- for-of -- Symbol.iterator
- instanceof -- Symbol.hasInstance
- async/await -- Symbol.asyncIterator
- 原始值转换 -- Symbol.toPrimitive
- 给对象设置'唯一值'属性名
- code
let key = Symbol('AA');//创建唯一值 let obj = { a:1, [key]:2, [Symbol('AA')]:3 } console.log(obj[key]);//2 console.log(obj[Symbol('AA')]);//undefined 每次Symbol创建的值都是唯一的,这种方式也是生成新的Symbol值 console.log(Symbol('AA') === Symbol('AA'));//false
- bigint
复杂数据类型[对象类型]
- 标准普通对象 object
- 标准特殊对象 Array、RegExp、Date、Math、Error...
- 非标准特殊对象 Number、String、Boolean...
- 可调用/执行对象 [函数] function
类型检测
通用型检测
typeof
- 检测数据类型[引用类型不准确]
- 原理:
- 前置:所有的数据类型,在计算机底层都是按照计算机位(64/32)的二进制值进行存储的
- 原理:根据数据存储于内存中的二进制值规律进行判断[效率高]。
- 二进制的前三位是0,认定为对象,若该对象存在call方法,则为
function,反之则为object - 0000... => null null值为64位0,所以null值会被认为是
object - 1开头 => 整数
- 010开头 => 浮点数
- 110 => 布尔值
- -2^30 undefined
- 未被声明的变量 => undefined
- 二进制的前三位是0,认定为对象,若该对象存在call方法,则为
- code
// 使用 typeof 1 'number' typeof NaN 'number' typeof Infinity 'number' typeof '' 'string' typeof null 'objcet' typeof true 'boolean' typeof undefined 'undefined' typeof function a(){} 'function' typeof Symbol() 'symbol' typeof 10n 'bigint' typeof [] 'object' typeof {} 'object'
- 原理:
instanceof
- 检测当前实例是否属于该类[无法区分对象与其他,一般情况下,对象原型是所有对象实例的基类]
- 原理:只要当前类出现在实例的原型链上,结果都是true
- 弊端:
- 由于对象可以随意修改原型,所以可能出现不准确的情况
- 不可鉴别出通过对象字面量创建的基本数据类型
- code
-
let arr = []; console.log(arr instanceof Array);// true console.log(arr instanceof Regexp);// false console.log(arr instanceof Object);// true let Fn = function Fn(){} Fn.prototype = Object.create(Array.prototype) // 修改构造函数原型 const fn = new Fn(); console.log(fn instanceof Array);// true -- 不准确 console.log(1 instanceof Number);// false -- 不准确 let num = new Number(1) console.log(num instanceof Number) ;// true -- 但是没用,基础类型基本不会这样声明 /* 手撕instanceof function _instanceof(example, classFn) { let classPrototype = classFn.prototype; let _proto = Object.getPrototypeOf(example); // 获取example.__proto__[浏览器下不允许直接这样获取] while (true) { if (_proto === null) { return false // 若是到头了,则说明没有找到 } if (_proto === classPrototype) { return true } _proto = Object.getPrototypeOf(_proto); // 获取原型上的__proto__,直至object } } let obj = {} console.log(_instanceof(obj, Array)) */
-
constructor
- 通过实例的构造器判断是否属于该类
- 优点:比instance准确,但也有弊端
- 弊端:与instanceof一样,constructor可以修改
- code
-
let arr = []; arr.constructor === Array;//true arr.constructor === Object;//false arr.constructor === Regexp;//false let num = 1; num.constructor === Number;// true
-
Object.prototype.toString.call
- 改变this指向,返回当前实例所属类的信息,并不是返回字符串[标准数据类型检测方法]
- 返回结果:
[object Number/String/Boolean/Object....] - code
-
Object.prototype.toString.call(1);//['object Number'] Object.prototype.toString.call(null);//['object Null']
-
- 返回结果:
特定类型检测
- Array.isArray
- isNaN
类型转换
#其他数据转数字
- Number[val]
- 场景
- 数字运算
- isNaN检测
- ==比较
- 规则
- 字符串转换为数字:空字符串转换为0,出现任何非数字字符,则转换为NaN
- 布尔值转换为数字:true -> 1,false -> 0
- null -> 0 undefined -> NaN
- Symbol无法转换为数字[error]
- BigInt去除n,若超过安全数的,按科学计数法处理
- 对象转换为数字(对象为x)
- 调用x[Symbol.toPrimitive],有则调用该方法并返回该方法返回值,无则继续往下。
- 调用x[valueOf],获取原始值
- 若x无原始值或原始值非数字,继续往下
- 若x为原始值数字,则直接返回
- 调用x[toString],所有的对象都继承自Object,所以都有toString方法,
- 最后将该字符串转换为数字
- 补充:
- 若把对象x转换为字符串,走上述规则且没有第四步
x.toString() == String(x),不一定相等,步骤不同。
- code
let time = new Date(); console.log(Number(time));// => 1.time[Symbol.toPrimitive]('number') => 当前时间戳 let arr = [10]; console.log(Number(arr)); // => 1. arr[Symbol.toPrimitive] => undefined; 2. arr[valueOf] undefined; 3.arr[toString] => '10' => Number('10') => 10 let num = new Number(10); console.log(Number(num));// 1.num[Symbol.toPrimitive] => undefined; 2. num.valueOf() => 10
- 场景
- parseInt & parseFloat
- 使用:parseInt(val,radix),parseFloat(val) -> 十进制转换,直接转为字符串
- 若val非字符串,则先隐式转换为字符串,通过
String(val)的方式 - radix为进制,默认为10
- 若不传或者传0,则使用默认值
- 若val以0x开头(16进制),则radix默认为16
- 有效范围:2~36,若传入值不在范围内,返回NaN
- 从val字符串左侧第一位开始查找,查找到符合radix进制的值(遇到不符合的则结束查找,不论后面是否有符合的),把符合的内容按照radix进制,转换为10进制返回。
- 按权展开求和 -- 根据位数乘幂相加
parseInt('103',5) => 1 * 5^2 + 0 * 5^1 + 3 * 5^0 =》 25 + 3 -> 28
- 按权展开求和 -- 根据位数乘幂相加
- 补充:JS遇到以0开始的数字,会先自动以8进制转为10进制
- 若val非字符串,则先隐式转换为字符串,通过
- code
console.log(parseInt('10102px',2)); // => 2进制只识别1/0,10102px => 1010 => 转为10进制 => 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 0 * 2^0 -> 10 // 字节面试题 let arr = [27.2,0,'0013','14px',123]; arr = arr.map(parseInt); console.log(arr); // [27,NaN,1,1,27] // 实际代码执行 arr.map((item,index)=>{ /* 27.2 - 0 => 0则默认10进制 ; 0 - 1 => 1不在进制范围,NaN ; '0013' - 2 => 二进制只能识别1/0 => 1 ; '14px' - 3 => 3进制只能识别0123 => 1 ; '123' - 4 => 1 * 4^2 + 2 * 4^1 + 3 * 4^0 => 16 + 8 + 3 => 27 */ return parseInt(item,index); }) - 使用:parseInt(val,radix),parseFloat(val) -> 十进制转换,直接转为字符串
- 其他类型转换String
- 非{},用''包裹
- {},Object.prototype.toString.call() -> '[object object]'
- 其他类型转布尔值
- 除了
0/NaN/''/null/undefined五个值为false,其余皆为true
- 除了
隐式转换
- +号拼接
- 当"+"号两端有一端是字符串或者某些对象,则先将对象转字符串,然后以字符串拼接的规则处理。
- "+"号出现一端
let a = '10'; a++ -> 10; a -> 11let a = '10';+a => 10
- 数字其他类型
- 布尔值 -> 1/0
- null -> 0
- undefined -> NaN -> 100 + NaN -> NaN
- code
console.log(10 + '10');//'1010' console.log(10 + new Number(10));// new Number(10)[Symbol.toPrimitive] -> undefined ;new Number(10).valueOf() -> 10; 10 + 10 -> 20 console.log(10 + new Date()); // new Date([Symbol.toPrimitive])('default') -> 'GMT date' -> 10 + 'GMT date' console.log(10 + [10]);// [10][Symbol.toPrimitive] -> undefined;[10].valueOf() -> [10]; [10].toString() -> '10'; 10 + '10' -> '1010' let result = 100 + true + 21.2 + null + undefined + 'Tencent' + [] + null; /* true -> 1 ; null -> 0; 数字规则:undefined; 100 + true -> 101; 101 + 21.2 -> 122.2 -> 122 +null -> 122 -> 122 + undefined -> NaN 字符串规则:NaN + 'Tencent' -> 'NaNTencent' -> 'NaNTencent' + [] -> 'NaNTencent' + null -> 'NaNTencentnull'*/ console.log(result);
- ==
- 两边数据类型不同,则需要先转为相同类型,再进行比较
- 对象 == 字符串 对象转字符串 [Symbol.toPrimitive] => valueOf() => toString() ;当某一步骤有原始值字符串,则直接使用
- null/undefined == undefined/null -> true ; null/undefined 和其他值都不相等
- null === undefined -> false
- 对象 == 对象,比较堆内存地址,地址相同则相等
- NaN !== NaN ; NaN和任何数都不相同,所以补充机制(Object.isNaN())
- 除了以上情况
- 若只要两边类型不同,统一转为数字,然后再进行
===比较,===比较,若两边类型不同,直接false,不会隐式转换。
- 若只要两边类型不同,统一转为数字,然后再进行
- code
undefined == undefined ; // (==/===) -> true null == null ; // (==/===) -> true console.log([] == false); // true; 对象 & 布尔值 => 都转为数字再 [] [Symbol.toPrimitive] => valueOf() => toString() => '' => 0 === => 0 === 0 -> true console.log(![] == false); // true;![] -> false; false === false -> true - 两边数据类型不同,则需要先转为相同类型,再进行比较
- 包装类 & 解包装类
- 当原始值调用方法是,会将原始值包装为对象调用方法
- 当原始值对象参与运算时,会将原始值对象解包装为原始值参与运算
- 以上两者皆为浏览器行为
- code
let num = 10; num.toFixed(2); // num => new Number(10).toFixed(2) => '10.00'; let num1 = new Number(10); num1 + num; // num1 => [Symbol.toPrimitive] => valueOf() => toString() ... => 10 + 10 => 20
思考题
(a ==1 && a== 2&& a==3)
/*
根据==判断会转换数据类型,判断出a为对象,则在其转换为数字的某一步骤重写方法即可
[Symbol.toPrimitive] => valueOf() => toString() => Number()
思路1:重写方法
思路2:数据劫持+外部变量
思路3:利用数组特性(开放性思路)
*/
// 当a为几,输出ok
if(a ==1 && a== 2&& a==3){
console.log('ok')
}
// 一般思路
var a = {
i:0,
[Symbol.toPrimitive](){
return ++this.i
}
// toString(){
// return ++this.i
// }
// valueOf(){
// return ++this.i
// }
};
// 大神思路
var a = [1,2,3];
a.toString = a.shift;// a.shift => 截取数组第一位
0.1 + 0.2 ?== 0.3;// 为什么不等于0.3,精准度丢失
/*
结论:JS中关于浮点数的计算会出现精度丢失的问题,计算机设计问题,非JS问题。
原因:JS中所有值都是以二进制在计算机中存储
整数转换为二进制是取余 -> 10 -> 10/2 = 5余0 => 5/2 = 2余1 => 2/2 = 1余0 => 1/2 =0余1 => 余数倒序拼接 -> 1010
浮点数转换为二进制是乘法取整,可能会出现无限循环的情况
0.1 * 2 = 0.2取0 => 0.2*2 = 0.4取0 => 0.4*0.2 = 0.8取0 => 0.8*2 = 1.6取1 => 0.6*2 = 1.2取1 => 0.2*2 = 0.4取0 ....
0 0 0 1 1 0 0 00
-> 000110001100011...
计算机底层存储时,是以64/32位存储的,所以就舍弃了一些值,所以值就失去了精准度
解决手法:
运算结果.toFix(2); (0.1 + 0.2).toFix(2); 再转为数字; +(0.1+0.2).toFix(2)
转成整数运算完再归成小数
*/