JavaScript数据类型和数据类型转换

631 阅读10分钟

JS 中有哪些数据类型

基本数据类型/值类型

  1. 数字 number

  2. 字符串 string

  3. 布尔 boolean

  4. 空 null

  5. 未定义 undefined

  6. 唯一值 symbol

  7. 大数据值 bigint

引用数据类型

  1. 对象 object

    • 数组 new Array
    • 正则 new RegExp
    • 日期 new Date
    • 错误 new Error
    • ...
  2. 函数 function

    有的人会认为函数 function 属于对象,其实确实也是,函数有三种角色,它既是一个普通函数,又可以作为一个普通对象来进行成员访问,它还能作为一个类。

String

// 字符串这个类型非常简单,只要单引号、双引号、“``”里包起来的就是字符串,不管里面的内容是什么
let str1 = ''
let str2 = ""
let str3 = '[10, 20]'
let str4 = 'function(){}'
let str5 = `` // es6新增的模板字符串,优势在于方便数据拼接
let str6 = `${}` // '${}'存放的是JS表达式:执行有返回结果的就是JS表达式(变量/数学运算/三元运算符/map...)

Boolean

let bool1 = true;
let bool2 = false;

Number

let num1 = 0;
let num2 = -1;
let num3 = 0.1;
let num4 = NaN; // not a number 不是一个有效数字,但是它确实是属于Numbe类型
let num5 = Infinity; // 无穷大

Symbol

Symbol 是 JavaScript原始数据类型 ,Symbol 实例是唯一且不可改变的

Symbol 虽然是首字母大写,但是他是没有 constructor 的,不可以被 new

Symbol('foo') !== Symbol('foo');
const foo = Symbol();
const bar = Symbol();
typeof foo === 'symbol';
typeof bar === 'symbol';
let obj = {};
obj[foo] = 'foo';
obj[bar] = 'bar';
JSON.stringify(obj); // {}
Object.keys(obj); // []
Object.getOwnPropertyNames(obj); // []
Object.getOwnPropertySymbols(obj); // [ foo, bar ]

BigInt

在 JavaScript 中,BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数。而在其他编程语言中,可以存在不同的数字类型,例如:整数、浮点数、双精度数或大斐波数。

JS 中有一个最大安全数

Number.MAX_SAFE_INTEGER
9007199254740991(2^53-1)
Number.MIN_SAFE_INTEGER
-9007199254740991(-(2^53-1))
// 超过这个最大安全数,就不能精准计算了
Number.MAX_SAFE_INTEGER + 1
Number.MAX_SAFE_INTEGER + 2
...
// 都将不一定得到准确数值,所以超过最大安全数,我们使用BigInt处理

// 要创建BigInt,只需要在整数的末尾追加n即可
console.log(9007199254740995n);    // → 9007199254740995n
console.log(9007199254740995);     // → 9007199254740996

// 或者,可以调用BigInt()函数(和Symbol一样,非构造函数,不可以new)
BigInt("9007199254740995");    // → 9007199254740995n

BigInt 实际中使用特别少,我们知道它是什么东西就够了,这里就不深究了,有兴趣的同学可以去看MDN-BigInt

上面简单介绍了 JS 中的几种基础数据类型,关于 function 和 object 类型将在后续函数的高级用法面象对象等文章中再详细介绍

数据类型检测

typeof

typeof 1; // 'number'
typeof NaN; // 'number'
typeof Infinity; // 'number'
typeof -Infinity; // 'number'
typeof ``; // 'string'
typeof true; // 'boolean'
typeof null; // 'object'
typeof undefined; // 'undefined'
typeof Symbol(); // 'symbol'
typeof BigInt(10); // 'bigint'
typeof {}; // 'object'
typeof []; // 'object'
typeof /^$/; // 'object'
typeof /^(?:-|\+)?(?:\d|(?:[1-9]\d+)(?:\.d+)?$/; // 'object'
typeof function () {}; // 'function'
  • typeof 检测结果都是字符串,字符串中包含对应的类型"object"
  • typeof null => 'object',但是 null 不是对象,为什么会出现这种情况呢?因为在 JS 的最初版本中,使用的是 32 位系统,为了性能考虑使用低位存储了变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。虽然现在的内部类型判断代码已经改变了,但是对于这个 Bug 却是一直流传下来。
  • 检测数组,正则,日期对象返回的都是”object“,所以无法基于此方法细分对象
typeof 相关面试题
const a = typeof typeof typeof [12, 23];

console.log(a);

// 解析
// typeof(typeof(typeof [12, 23])) => typeof(typeof 'object') => typeof('string') => 'string'
NaN
// NaN: not a number 不是一个有效数字,但是它属于number类型
NaN === NaN; // false,NaN不是一个有效数字,所以它只要不是一个有效数字,其他啥都有可能,他和所有值都不相等,包括他自己

let n = '10';
if (n !== NaN) {
    // 条件永远成立,使用这种方式,判断变量是不是有效数字,是不可以的
    console.log('n不是有效数字');
}

// 上面那种方式去判断变量是否是有效数字,肯定是不行的,我们需要用到isNaN
// isNaN([val]):是否为非有效数字,把[val]隐式转换【Number([val])】为数字类型,再看一下是否为非有效数字,如果确实是非有效数字,返回TRUE,隐式转换后确实是有效数字,结果返回FALSE
let n = 'AAA';
if (isNaN(n)) {
    // 所以,要使用这种方式去判断变量是不是有效数字
    console.log('n不是有效数字');
}
NaN 相关面试题
let res = parseFloat('left:200px'); // parseFloat转换得出结果NaN
if (res === 200) {
    // NaN === 200 不成立
    alert(200);
} else if (res === NaN) {
    // NaN === NaN 不成立 NaN不等于任何值,包括它本身
    alert(NaN);
} else if (typeof res === 'number') {
    // NaN是一个数字类型,条件成立
    alert('number');
} else {
    alert('Invalid Number');
}
// => 基于alert输出一个值,都要把值隐式转换为字符串,然后在输出

JS 中的数据类型转换

把其他数据类型转换为 Number 类型

// 1. 特定需要转换为Numbe的(Number([val])、parseInt/parseFloat([val]))
// 2. 隐式类型转换(浏览器内部默认要先转换为Number类型在进行计算的)【基本上都是基于Number([val])】来完成的
//    + isNaN([val])
//    + 数学运算(特殊情况:’+‘加号在出现字符串的情况下不是数学运算,是字符串拼接
//    + 在‘==’两个等号进行比较时,有些值需要转换为数字再进行比较
//    + ...
Number
  1. 把字符串转换为 Number,要求字符串中所有字符都必须是有效数字才能转换
console.log(Number('')); // 0
console.log(Number('10')); // 10
console.log(Number('10px')); // NaN
  1. 把布尔值转换为数字
console.log(Number(true)); // 1
console.log(Number(false)); // 0
  1. 把空转换为数字
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
  1. 把 Symbol、BigInt 转换为数字
console.log(Number(Number(Symbol('')))); // Uncaught TypeError: Cannot convert a Symbol value to a number
console.log(Number(BigInt('10'))); // 10
console.log(Number(BigInt('10px'))); // Uncaught SyntaxError: Cannot convert 10px to a BigInt
  1. 把对象或者函数转换为数字(先要基于 toString 把对象转换为字符串,再把字符串转化为数字)
console.log(Number({ 0: 10 })); // NaN
// ({0: 10}).toString() => '[object Object]',普通对象.toString()是检测数据类型,返回'[object Object]',再转换为数字为NaN

console.log(Number([10])); // 10
// [10].toString() => '10', Number('10') => 10

console.log(Number([10, 20])); // NaN
// [10, 20].toString() => '10, 20',Number('10,20') => NaN
// 数组.toString()是转换为字符串

console.log(Number([])); // 0 [].toString() => '' Number('') => 0
parseInt/parseFloat([val])

规则:先把[val]值转换为字符串,再按照从字符串左边第一个字符开始查找,把所有找到的有效数字字符串变为数字,直到遇到一个非有效数字字符则停止查找,如果一个有效数字字符都没有找到,返回结果就是 NaN,parseFloat 相比 parseInt 只是多识别一个小数点而已

console.log(parseInt('10px')); // 10
console.log(parseInt('10.5px')); // 10
console.log(parseFloat('10.5px')); // 10.5
console.log(parseInt('width:10px')); // NaN

把其他数据类型转换为字符串

// 1. 能使用的办法
//    + String()
//    + toString()
// 2. 隐式转换(一般都是调用其toString())
// 		+ 在加号运算的时候,如果加号的某一边出现字符串,则不是数学运算,而是字符串拼接
// 		+ 把对象转换为数字,需要先toString()转换为字符串,再去转换为数字
//    + 基于alert/confirm/prompt/document.write... 基于这些方式输出内容,都是把内容先转换为字符串,然后再输出
//    + ...
toString()
// 除了普通对象.toString()是检测数据类型(因为他们调用的是Object.prototype.toString(), 这个方法是检测数据类型的,其余的都是调用自己类原型上的toString,也就是用来转换字符串的)
let obj = { a: 1 };
let arr = [10];
let reg = /^\d+$/;

console.log(obj.toString()); // '[object Object]'
console.log(arr.toString()); // '10'
console.log(reg.toString()); // '/^\d+$/'

把其他类型转换为布尔

// 1. 基于以下方式可以把其他数据类型转换为布尔
//    + ! 转换为布尔值后取反
//    + !! 转换为布尔类型
//    + Boolean([val])
// 2. 隐式转换
//    + 在循环或者条件判断中,条件处理的结果是布尔值类型值
//  规则:只有0、NaN、null、undefined、空字符串,五个值变为布尔的FALSE,其余都是TRUE

console.log(!0); // true
console.log(!!0); // false
console.log(!![]); // true,空数组不会先转换为空字符串,记住那5个值

if (1) {
    // 先把1转换为布尔,再验证真假
}
if (3 - 'AA' > 0) {
    // 运算结果转换为布尔
}

验证一个对象是否为空对象

function isEmptyObject(obj) {
    if (obj === null || typeof obj !== 'object') return false;
    let flag = true;
    for (let key in obj) {
        if (!obj.hasOwnProperty(key)) break; // 没有自己的私有属性
        flag = false;
        break;
    }
    return flag;
}
隐式类型转换相关面试题
let result = 10 + false + undefined + [] + 'Tencent' + null + true + {};
console.log(result);

// 解析:
// ’+‘加号在js当中不一定是数学运算,只要有一边出现字符串(或者对象【因为数字 + 对象,理论上是吧对象转换为数字进行运算,但是我们知道,对象转换为数字要先转换为字符串,这样在还没有转换了数字的时候,就遇到了字符串了,变为字符串拼接】),都要变成字符串拼接

console.log(1 + '10'); // '110'
console.log(1 + [10]); // '110' [10].toString() => '10'
console.log(1 - [10]); // -9 加号拼接,减号还是做数学运算

// 所以上题
// 10 + false => 10 + Number(false) => 10 + 0 => 10
// 10 + undefined => 10 + Number(undefined) => 10 + NaN => NaN
// NaN + [] => NaN + Number([]) =>  NaN + [].toString() => 'NaN'
// 'NaN' + 'Tencent' => 'NaNTencent'
// 'NaNTencent' + null + true => 'NaNTencentnulltrue'
// 'NaNTencentnulltrue' + {} => 'NaNTencentnulltrue' + ({}).toString()
// 'NaNTencentnulltrue[object Object]'
显示类型转换相关面试题
parseInt(''); // NaN,parseInt的规则是从字符串从左往右找,一个都没找到就是NaN

Number(''); // 0, Number是从左往右,只要出现一个非有效数字就是NaN

isNaN(''); // false,isNaN(Number('')) => isNaN(0) => false

parseInt(null); // NaN,parseInt(null.toString()) => parseInt('null') => NaN

Number(null); // 0

isNaN(null); // false,isNaN(Number(null)) => isNaN(0) => false

parseInt('12px'); // 12

Number('12px'); // NaN

isNaN('12px'); // true

parseFloat('1.6px') + parseInt('1.2px') + typeof parseInt(null);
// 1.6 + 1 + 'number' => 2.6number

isNaN(Number(!!Number(parseInt(0.8))));
// isNaN(Number(!!Number(0))) => isNaN(Number(false)) => false

typeof !parseInt(null) + !isNaN(null);
// typeof的运算优先级要高于加号
// typeof false + false => boolean + !isNaN(0) => boolean + true => 'booleantrue'
// 浏览器运算符优先级 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

在 JS 中的比较操作

// == 比较
// === 绝对比较 (switch case 基于===比较的)
// 如果左右两边数据类型不一致,==会默认把数据类型转换为一致的,再去进行比较,===则直接返回false,因为它要求数据类型和值都一样才相等(严谨)

在==比较的过程中,数据转换的规则:

// 类型一样的几个特殊点
{} == {} // false,对象比较的是堆内存的地址
[] == [] // false
NaN == NaN // false

// 类型不一样的转换规则
// 1.
null == undefined // true,剩下的null/undefined和其他任何数据类型值都不相等
null === undefined // false,

// 2. 字符串 == 对象,要把对象转换成字符串

// 3. 剩下如果 == 两边数据类型不一致,都是需要转换为数字再进行比较
console.log(2 == true); // false
// 规则:== 是将数据类型转成一致再比较,将true转换成数字 2 == 1 => false
面试题
console.log([] == false); // true
// 两边类型不一致,转换成数字然后再比较,Number([]) => Number('') => 0
// Number(false) => 0

console.log(![] == false); // true
// 除0、NaN、null, undefined、空字符串变为false,其他全是true,所以![] => false
// false == false => true