js中的基本数据类型和之间的相互转换

181 阅读10分钟

数据类型的分类

  • 原始值类型【值类型/基本数据类型】
    • number 数字
    • string 字符串
    • boolean 布尔
    • null 空对象指针
    • undefined 未定义
    • symbol 唯一值
    • bigint 大数
  • 对象类型【引用数据类型】
    • 标准普通对象 object
    • 标准特殊对象 Array、RegExp、Date、Math、Error...
    • 非标准特殊对象 Number, String, Boolean...
    • 可调用执行对象【函数】function

symbol 创建唯一值, 创建唯一值时不能new

+ 给对象设置“唯一值”的属性名
    + 字符串
    + Symbol类型
    + Map新的数据结构:可以允许属性名是对象
+ Symbol.asyncIterator/iterator/hasInstance/toPrimitive/toStringTag...是某些JS知识底层实现的机制
+ 在派发行为标识统一进行管理的时候,可以基于symbol类型的值,保证行为标识的唯一性
+ ...

let a1 = Symbol('AA');
let a2 = Symbol('AA');
let a3 = a1;
console.log(a1 === a2); // false
console.log(a1 === a3); // true

let key = Symbol('BB');
let obj = {
	n: 10,
	10: 100,
	true: 200,
	[Symbol('AA')]: 300,
	[Symbol('AA')]: 600,
	[key]: 400
}
obj[10] // 100
obj['10'] // 100
console.log(obj[Symbol('AA')]); // undefined
console.log(obj[key]); // 400

BigInt 大数类型

Number.MAX_SAFE_INTEGER 9007199254740991 JS中的最大安全数
Number.MIN_SAFE_INTEGER -9007199254740991 最小安全数
超过安全数字后,再进行运算或者访问,结果会不准确!!!

解决方案:
    1. 服务器返回给客户端的大数,按照“字符串”格式返回!
    2. 客服端把其变为BigInt,然后按照BigInt进行运算
    3. 最后把运算后的BigInt转为字符串,在传递给服务器即可
console.log(BigInt('9007199254740991123123') + BigInt(12345)); // 9007199254740991135468n
console.log((9007199254740991135468n).toString()); // 9007199254740991135468

数据类型检测

  • typeof 检测数据类型的逻辑运算符
  • instanceof 检测是否为某个类的实例
  • constructor 检测构造函数
  • Object.prototype.toString.call 检测数据类型的

  • Array.isArray()
  • isNaN
  • ...

1. typeof[value] 返回当前值的数据类型 "数据类型"

  • 所有的数据类型值,在计算机底层都是按照“64位” 的二进制进行存储的
  • typeof是按照二进制值进行检测类型的
    • 二进制的前三位是零,认为是对象,然后再去看有没有实现call方法,如果实现了,返回'function', 没有实现,则返回'object'【typeof 不能细分对象类型(检测普通对象或者数组对象等都是"object")】
    • null是64个零 typeof null -> 'object'【局限性】
    • ...
  • 检测未被声明的变量,值是'undefined'
  • 返回的结果都是字符串
console.log(a); // Uncaught ReferenceError: a is not defined
console.log(typeof a); // 'undefined'

// 场景一:检测当前值是否是一个对象
const fn = options => {
	let type = typeof options;
	if (options !== null && (type === "object" || type === "function")) {
		// 是个对象
	}
}
fn({
	x: 10,
	y: 20
});
fn(10);

// 场景二:支持更多的模块导入方案
(function () {
	let utils = {};
	if (typeof window !== "undefined") window.utils = utils;

	if (typeof module === "object" && typeof module.exports === "object") module.exports = utils
})();

2. 面试技巧总结: 两个及两个以上的typof都是字符串'string'

let a = typeof typeof typeof [12, 23];
console.log(a); //=>"string"
/*
 *  typeof [12, 23] =>"object"
 *  typeof "object" =>"string"
 *  ...
 */

把其它数据类型转换为数字的方法Number

  • 隐式转换: 浏览器默认去转化使用Number([val])
  • 显示转换: Number([val]), parseInt/parseFloat

Number([val])

  • 一般用于浏览器的隐式转换
      1. 数学运算
      1. isNaN检测
      1. ==比较 ...
  • 规则
    • 字符串转为数字:空字符串变为0,如果出现任何非有效数字字符,结果都是NaN
    • 把布尔转换为数字:true->1 false->0
    • null->0 undefined->NaN
    • Symbol 无法转为数字,会报错:Uncaught TypeError: Cannot convert a Symbol value to a number
    • BigInt 去掉“n”(超过安全数的,会按照科学技法处理)
    • 把对象转为数字:
      • 先调用对象的 Symbol.toPrimitive 这个方法,如果不存在这个方法
      • 再调用对象的 valueOf 获取原始值,如果获取的值不是原始值
      • 再调用对象的 toString 把其变为字符串
      • 最后再把字符串基于 Number 方法转为数字
    • NaN 不是一个有效数字,但是属于Number类型
    • infinity 无穷大, -infinity无穷小,都是Number类型
let time = new Date();
console.log(Number(time));
/* 
    首先检测Symbol.toPrimitive 有没有,结果:有,而且是个函数
    time[Symbol.toPrimitive]('number')
 */

let arr = [10];
console.log(Number(arr));
/* 
    首先 arr[Symbol.toPrimitive] => undefined
    其次 arr.valueOf() 获取原值 => [10] 不是原始值
    再次 arr.toString() 转换为字符串 => '10'
    最后 再把字符串'10'转换为数字 => 10
 */

let num = new Number(10);
console.log(Number(num));
/* 
    首先 num[Symbol.toPrimitive] => undefined
    其次 num.valueOf() => 10
 */

NaN!=NaN 它和谁都不相等,包括和自己本身也不相等

if ("珠峰" == NaN) {
    // 条件是否成立?  NaN!=NaN  它和谁都不相等,包括和自己本身也不相等
}

isNaN(值) 检测这个值是否为有效数字,如果不是有效数字返回TRUE,是有效数字返回FALSE

let res = parseFloat('left:200px'); //=>NaN
if (res === 200) {
    alert(200);
} else if (res === NaN) { //NaN!=NaN
    alert(NaN);
} else if (typeof res === 'number') { //=>typeof NaN => "number"
    alert('number');
} else {
    alert('Invalid Number');
}
//=>number

parseInt([val],[radix])

  • [val]必须是字符串,如果不是,要先隐式转换为字符串String([val])
  • [radix]进制 + 如果不写,或者写零:默认是10(特殊情况:如果字符串是以0x开始的,默认值是16进制) + 有效范围:2~36之间(如果不在这个范围,结果直接是NaN)
  • 从[val]字符串左侧第一个字符串开始查找,查找出符合[radix]进制的值(遇 到不符合则结束查找,不论后面是否还有符合); 把找到的内容,按照[radix] 进制,转化为10进制 任何数的0次幂都是1
console.log(parseInt('10102px13', 2)) // 10
// 找到符合二进制的值 '1010'
// 把这个二进制的值转换为10进制 “按权展开求和”
// 1*2^3+0*2^2+1*2^1+0*2^0 => 8+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 => {}))
map会返回一个新的数组,不会覆盖原数组
相当于把item和index作为参数传给parseInt
parseInt(27.2, 0) => parseInt('27.2', 10)
    '27' 把其当做10进制转换为10进制 => 27
parseInt(0, 1) => 第二个参数是2到36之间,
    直接NaN
parseInt('0013', 2) => 把其当做2进制,返回10进制
    '001' => 0*2^2+0*2^1+1*2^0 => 0 + 0 + 1 => 1
parseInt('14px', 3) => 把其当做3进制,返回10进制
    '1' => 1*3^0 => 1
parseInt(123, 4) => 把其当做4进制,返回10进制
    '123' => 1*4^2+2*4^1+3*4^0 => 16+8+3 =>27
 */

// JS中遇到以0开始的“数字”,会默认把其按照8进制转为10进制,然后在进行其他操作
console.log(parseInt(0013, 2)); //=>3
// 先8转10 0+*8^3+0+*8^2+1+*8^1+3*8^0 => 0+0+8+3 = 11
// parseInt('11', 2)
// '11'
//  再2转10 1*2^1+1*2^0 => 2+1 => 3
console.log(parseInt('0013', 2)); //=>1
// 本省就是字符串,直接把0013按2进制计算,返回10进制
// 001 => 0*2^0+0*2^1+1*2^0 => 1 

Number 直接调用浏览器最底层的数据类型检测机制来完成(以下是基于Number隐式转换的)

  • true => 1

  • false => 0

  • null => 0

  • ''=> 0

  • undefined => NaN

  • 字符串中必须保证都是有效数字才会转换为数字,否则都是NaN

  • isNaN(数字) 先转为数字(隐式Number)

  • isNaN('') //=>false

parseInt("") //NaN
Number("") //0
isNaN("") //先把""转换为数字(隐式 Number)  isNaN(0)  false
parseInt(null) //parseInt('null') NaN
Number(null) //0
isNaN(null) // isNaN(0)  false
parseInt("12px") //12
Number("12px") //NaN
isNaN("12px") //isNaN(NaN) true
parseFloat("1.6px") + parseInt("1.2px") + typeof parseInt(null);        //1.6 + 1 + typeof NaN  => 2.6 + 'number'  -> '2.6number'
        
    
isNaN(Number(!!Number(parseInt("0.8"))));
//=> isNaN(Number(!!Number(0))); 
//=> isNaN(Number(!!0))
//=> isNaN(Number(false))
//=> isNaN(0)
//=> false


typeof !parseInt(null) + !isNaN(null);
//=> typeof !NaN + !isNaN(0)
//=> typeof true + true
//=> 'boolean' + true
//=> 'booleantrue'
        
        
// []==true 都转换为数字
// Number([])  Number('')  0
// 1

let result = 10 + false + undefined + [] + 'Tencent' + null + true + {};
console.log(result);  //=>'NaNTencentnulltrue[object Object]'
//=> 10 + 1 +NaN
//=> NaN + undefined
//=> NaN + []
//=> 'NaN' + 'Tencent'
//=> 'NaNTencent' + null
//=> 'NaNTencentnull' + true
//=> 'NaNTencentnulltrue' + {}
//=> 'NaNTencentnulltrue[object Object]'


[] == true //都转为数字
//=> 左侧 Number([]) => Number('') =>0
//=> 右侧 1
//=> 结果:false

把其他类型数据转为String

  • 转换规则
    • 拿字符串包起来
    • 特殊:Object.prototype.toString
  • 出现情况
    • String([val]) 或者[val].toString() 这两个不一样(尤其是对object转换)
    • “+”除数学运算,还可能代表字符串拼接
    • “+”有两边,一边是字符串,会以字符串拼接的规则处理
    • “+”有两边,一边是对象,会以字符串拼接的规则处理
    • “+”只出现在左边,转换为数字
/* 
    “+” 出现左右“两边”,其中一边是字符串,或者是某些对象:会以字符串拼接的规则处理
    “+” 出现在一个值的左边,转换为数字
 */
let num = '10';
console.log(+num); //=> 10

let i = '10';
i++; // 一定是数学运算
console.log(i) //=> 11

i = '10';
i += 1;
console.log(i); //=> '101'

i = '10';
i = i + 1;
console.log(i); //=> '101'

let result = 100 + true + 21.2 + null + undefined + 'Tencent' + [] + null + 9 + false
console.log(result) //=> NaNTnecentnull9false
// 100+1+21.2+0+0+NaN -> NaNTnecentnull9false

把其他类型的数据转换为Boolean

  • 转换规则: 除了“0/NaN/空字符串/null/undefined” 五个值都是false, 其余都是true
  • 出现状况:
    • Boolean([val]) 或者 !/!!
    • 条件判断
console.log([] == false) // true
// 都转为数字:0 == 0
console.log(![] == false) // true
// 先处理![] => false false == false => true
console.log([] == ![]) // true

“==”比较时的相互转换规则

  • 对象==字符串 对象转换为字符串【Symbol.toPrimitive->valueOf->toString】
  • null==undefined =>true null/undefined 和其他任何值都不相等 null===undefined =>false
  • 对象==对象 比较的是堆内存地址,地址相同则相等
  • NaN!==NaN
  • 除了以上情况,只要两边类型不一致,剩下的都是转换为数字,然后在进行比较的 “===”绝对相等,如果两边类型不同,则直接是false,不会转换数据类型【项目中推荐使用的方法】
console.log(NaN == NaN); // false
console.log(Object.is(NaN, NaN)); // true

const fn = (num) => {
    if (num == null) {
            // mum 是null 或者 undefined
    }
}

普通对象转为字符串都是[object Object] 数组转字符串
->[]=>''
->[1,2,3] => "1,2,3"

[]==![]

  • !的优先级要大于==的,所以先运算右边。
  • !可将变量转换成boolean类型,0、null、undefined、NaN以及空字符串('')取反都为true,其余都为false
  • [], 取反是false
[]==false   //都转换为数字
[]先转换为''(toString)  在转换为数字0(Number)
false 变为数字0
![]==false
运算符优先级   ![]   再算比较
![]  转换为布尔值进行取反(把其它类型转换为布尔类型遵循的规律: 只有 0/NaN/null/undefined/'' 五个值是false,其余的都是true)  => false

false == false    true

{} +0

大括号在JS中太特殊了:对象、代码块(块作用域) 此处把大括号当做一个代码块 后面是+0

+值 这个操作是数学运算(不论加谁)
(+ +值) ( 值++) 都是数学运算

大括号在运算符前面

  • 在没有使用小括号处理优先级的情况下 不认为是数学运算,加小括号才算
  • 出现在运算符的后面 认为是数学运算

装箱与拆箱

let num = 10;
console.log(num.toFixed(2)); // 10.00
// num是原始值,不是对象,按常理来讲,是不能做“成员访问”
// 默认装箱操作: new Number(num) 变为非标准特殊对象, 这样就可以调用toFixed了

num = new Number(10);
console.log(num + 10); // 20
// 在操作的过程中,浏览器回把num这个非标准特殊对象编制原始值
//【Symbol.toPrimitive->valueOf->toString】,这个操作叫做拆箱

在JS中对象的属性名是什么格式的?

  • 普通对象的属性名只能是“字符串”(普通对象的属性名可以是基本数据类型值)
  • Map 这种数据结构支持 对象作为属性名
  • 但是普通对象的属性名不能是对象,如果是对象,需要转换为字符串存储
  • 普通对象toString 是调取 Object.prototype.toString 是用来检测数据类型的