深究JS底层原理
一、JS中八种数据类型判断方法
在JavaScript中,数据类型分为两大类:基本(原始)数据类型和引用(对象)数据类型。
基本数据类型(Primitive Data Types)
基本数据类型是表示简单的数据片段,它们是直接存储在变量中的数据。JavaScript中的基本数据类型有以下几种:
- String - 表示文本数据,例如
"Hello, World!"
。 - Number - 表示整数和浮点数,例如
42
和3.14
。 - Boolean - 表示逻辑值
true
或false
。 - Undefined - 表示未定义的值,即变量已声明但未初始化时的值。
- Null - 表示空值或者不存在的对象。在JavaScript中,
null
是一个表示“无”的对象,转为数值时为0
。 - Symbol (ES6新增) - 表示独一无二的值,常用于创建对象的私有成员。
- BigInt (ES2020新增) - 表示非常大的整数。
基本数据类型的值是不可变的,也就是说,一旦创建,就不能更改。如果对基本数据类型的变量进行操作,实际上是创建了一个新的变量或值。
引用数据类型(Reference Data Types)
引用数据类型表示的是数据的集合,它们存储的是对数据的引用(内存地址),而不是数据本身。在JavaScript中,最常见的引用数据类型是对象(Object),还包括以下几种:
- Object - 表示一组无序的键值对,例如
{name: "Alice", age: 25}
。 - Array - 表示一组有序的数据集合,例如
[1, 2, 3, 4]
。 - Function - 表示函数,JavaScript中的函数是一等公民,可以作为对象使用。
- Date - 表示日期和时间。
- RegExp - 表示正则表达式。
引用数据类型的值是可变的,这意味着可以改变它们的内容而不改变它们的身份。例如,可以向对象添加属性或修改数组中的元素。
基本数据类型与引用数据类型的区别
以下是一些基本数据类型和引用数据类型之间的关键区别:
- 存储方式:基本数据类型的值直接存储在变量中,而引用数据类型存储的是内存地址。
- 赋值:基本数据类型赋值时是传递值,而引用数据类型赋值时是传递引用。
- 比较:基本数据类型比较的是值,而引用数据类型比较的是引用地址。
- 参数传递:在函数中,基本数据类型作为参数传递时,传递的是值的副本;引用数据类型传递的是引用地址。
理解这些差异对于编写高效和正确的JavaScript代码非常重要。
八种数据类型判断方式
二、NaN
三、数值千分位的六种方法
1.format with array
(number + '')
是一种常见的类型转换技巧,它将数字 number
转换为字符串。无论 number
是什么数字,加上一个空字符串 ''
后,结果都会是一个字符串形式的 number。
function format_with_array(number) {// 转为字符串,并按照.拆分var arr = (number + '').split('.');// 整数部分再拆分var int = arr[0].split('');// 小数部分var fraction = arr[1] || '';// 返回的变量var r = "";var len = int.length;// 倒叙并遍历int.reverse().forEach(function (v, i) {// 非第一位并且是位值是3的倍数, 添加 ","if (i !== 0 && i % 3 === 0) {r = v + "," + r;} else {// 正常添加字符r = v + r;}})// 整数部分和小数部分拼接return r + (!!fraction ? "." + fraction : '');
}const print = console.log;
print(format_with_array(938765432.02));938765432.02
-
这行代码首先通过var arr = (number + '').split('.');
number + ''
将数字number
转换为字符串。这是因为split
方法是字符串的方法,不能直接在数字上使用。
然后使用split('.')
方法将字符串按照小数点(.
)分割成两部分,结果存储在数组arr
中。如果number
是一个整数,那么arr
将只有一个元素,即整数部分;如果number
是一个小数,那么arr
将有两个元素,第一个元素是整数部分,第二个元素是小数部分。 -
这行代码将var int = arr[0].split('');
arr
数组中的第一个元素(即整数部分)再次使用split('')
方法进行分割。由于没有指定分隔符,实际上是将整数部分的每个字符分割成一个单独的字符串,并将结果存储在数组int
中。例如,如果整数部分是"123"
,那么int
将是["1", "2", "3"]
。 -
这行代码尝试获取var fraction = arr[1] || '';
arr
数组中的第二个元素(即小数部分)。如果arr
只有一个元素(即number
是一个整数),那么arr[1]
将是undefined
。
使用逻辑或运算符||
,如果arr[1]
是undefined
(即没有小数部分),则将fraction
设置为一个空字符串''
。如果存在小数部分,则fraction
将是小数部分的字符串。
return r + (!!fraction ? "." + fraction : '');
-
r +
:这是字符串拼接的开始,将数字r
转换为字符串并准备拼接。 -
!!fraction
:这是双重逻辑非操作符,用于将fraction
转换为布尔值。如果fraction
是非空字符串、非零数字或任何“真值”对象,则!!fraction
将为true
;如果fraction
是空字符串、零、null
、undefined
或任何“假值”,则!!fraction
将为false
。 -
? "." + fraction : ''
:这是一个条件(三元)运算符,它根据!!fraction
的结果来决定返回的值。如果!!fraction
为true
(即fraction
有值),则返回"." + fraction
,即小数点加上fraction
的值;如果!!fraction
为false
(即fraction
没有值或为假值),则返回空字符串''
。
整体来看,这段代码的作用是:
如果 fraction
有值,那么返回的字符串将是 r
后面跟着一个小数点和 fraction
的值。如果 fraction
没有值或为假值,那么返回的字符串就只是 r
转换成的字符串。
2.format with substring
function format_with_substring(number) {// 数字转字符串, 并按照 .分割var arr = (number + '').split('.');var int = arr[0] + '';var fraction = arr[1] || '';// 多余的位数var f = int.length % 3;// 获取多余的位数,f可能是0, 即r可能是空字符串var r = int.substring(0, f);// 每三位添加","和对应的字符for (var i = 0; i < Math.floor(int.length / 3); i++) {r += ',' + int.substring(f + i * 3, f + (i + 1) * 3)}//多余的位数,上面if (f === 0) {r = r.substring(1);}// 整数部分和小数部分拼接return r + (!!fraction ? "." + fraction : '');
}const print = console.log;
print(format_with_substring(938765432.02));
3.format with mod
function format_with_mod(number) {var n = number; // 将传入的参数 number 赋值给变量 n,用于后续计算,保留原始数值不变。var r = ""; // 初始化一个空字符串 r,用于存储格式化后的结果。var temp; // 声明一个变量 temp,用于暂存计算过程中的中间值。do {mod = n % 1000; // 计算 n 除以 1000 的余数,即取 n 的最后三位数字(包括小数部分)。n = n / 1000; // 将 n 除以 1000,即每次循环处理掉 n 的最后三位数字。temp = ~~mod; // 使用双按位非操作符(~~)来取整,去除 mod 的小数部分,获取整数部分。// 以下代码用于构造格式化字符串:// 如果 n 大于等于 1,说明还有更多的数字需要处理,因此使用 padStart(3, "0") 来确保 temp 是三位数字,不足部分用 "0" 填充。// 如果 r 已经有内容,则在 temp 前面加上 ",",否则直接使用 temp。r = (n >= 1 ? `${temp}`.padStart(3, "0") : temp) + (!!r ? "," + r : "");} while (n >= 1); // 当 n 大于等于 1 时,继续循环,直到 n 小于 1。var strNumber = number + ""; // 将数字 number 转换为字符串,以便后续查找小数点位置。var index = strNumber.indexOf("."); // 查找小数点的位置。// 如果存在小数点(index >= 0),则将原始数字的小数部分添加到格式化字符串 r 的末尾。if (index >= 0) {r += strNumber.substring(index);}return r; // 返回格式化后的字符串。
}const print = console.log; // 将 console.log 方法赋值给变量 print,便于后续使用。
print(format_with_mod(38765432.02)); // 调用 format_with_mod 函数并打印结果。
4.format with regex
const print = console.log;
print(/hello (?=[a-z]+)/.test("hello a"));
print(/hello (?=[a-z]+)/.test("hello 1"));
function format_with_regex(number) {var reg = /\d{1,3}(?=(\d{3})+$)/g;return (number + '').replace(reg, '$&,');
}// 987, 654, 321// const print = console.log;
// print(format_with_regex(987654321));function format_with_regex(number) {var reg = /\d{1,3}(?=(\d{3})+$)/g;return (number + '').replace(reg, function(match, ...args){ console.log(match, ...args);return match + ','});
}
format_with_regex(987654321)// function format_with_regex(number) {
// var reg = /(\d)(?=(?:\d{3})+$)/g
// return (number + '').replace(reg, '$1,');
// }
5.format with toLocaleString
function format_with_toLocaleString(number, minimumFractionDigits, maximumFractionDigits) {minimumFractionDigits = minimumFractionDigits || 2;maximumFractionDigits = (maximumFractionDigits || 2);maximumFractionDigits = Math.max(minimumFractionDigits, maximumFractionDigits);return number.toLocaleString("en-us", {maximumFractionDigits: maximumFractionDigits || 2,minimumFractionDigits: minimumFractionDigits || 2})
}function format_with_toLocaleString(number) {return number.toLocaleString("en-us")
}const print = console.log;
print(format_with_toLocaleString(938765432.02));
6.Intl.NumberFormat
function format_with_Intl(number, minimumFractionDigits, maximumFractionDigits) {minimumFractionDigits = minimumFractionDigits || 2;maximumFractionDigits = (maximumFractionDigits || 2);maximumFractionDigits = Math.max(minimumFractionDigits, maximumFractionDigits);return new Intl.NumberFormat('en-us', {maximumFractionDigits: maximumFractionDigits || 2,minimumFractionDigits: minimumFractionDigits || 2}).format(number)
}// 使用默认配置选项
// function format_with_Intl(number) {
// return new Intl.NumberFormat('en-us').format(number)
// }const print = console.log;
print(format_with_Intl(938765432.02));
7.效果对比
对比一下这六种方法的性能
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>数字千分位</title>
</head><body><div style="text-align:center"><p><input type="number" value="5000" id="textCount" /></p><p><input type="button" onclick="executeTest()" value="测试" /><input type="button" onclick="javascript:document.getElementById('messageEl').innerHTML=''" value="清除" /></p></div><div id="messageEl" style="width: 300px;margin: auto;"></div><script>function format_with_array(number) {var arr = (number + '').split('.');var int = arr[0].split('');var fraction = arr[1] || '';var r = "";var len = int.length;int.reverse().forEach(function(v, i) {if (i !== 0 && i % 3 === 0) {r = v + "," + r;} else {r = v + r;}})return r + (!!fraction ? "." + fraction : '');}</script><script>function format_with_substring(number) {var arr = (number + '').split('.');var int = arr[0] + '';var fraction = arr[1] || '';var f = int.length % 3;var r = int.substring(0, f);for (var i = 0; i < Math.floor(int.length / 3); i++) {r += ',' + int.substring(f + i * 3, f + (i + 1) * 3)}if (f === 0) {r = r.substring(1);}return r + (!!fraction ? "." + fraction : '');}</script><script>function format_with_mod(number) {var n = number;var r = "";var temp;var mod;do {mod = n % 1000;n = n / 1000;temp = ~~mod;r = (n > 1 ? `${temp}`.padStart(3, "0") : temp) + (!!r ? "," + r : "")} while (n > 1)var strNumber = number + "";var index = strNumber.indexOf(".");if (index > 0) {r += strNumber.substring(index);}return r;}</script><script>function format_with_regex(number) {var reg = /(\d{1,3})(?=(\d{3})+(?:$|\.))/g;// var reg = /(\d)(?=(?:\d{3})+$)/g//var reg = /\d{1,3}(?=(\d{3})+$)/g;return (number + '').replace(reg, '$1,');}</script><script>// function format_with_toLocaleString(number, minimumFractionDigits, maximumFractionDigits) {// minimumFractionDigits = minimumFractionDigits || 2;// maximumFractionDigits = (maximumFractionDigits || 2);// maximumFractionDigits = Math.max(minimumFractionDigits, maximumFractionDigits);// return number.toLocaleString("en-us", {// maximumFractionDigits: maximumFractionDigits || 2,// minimumFractionDigits: minimumFractionDigits || 2// })// }function format_with_toLocaleString(number) {return number.toLocaleString("en-us")}</script><script>// function format_with_Intl(number, minimumFractionDigits, maximumFractionDigits) {// minimumFractionDigits = minimumFractionDigits || 2;// maximumFractionDigits = (maximumFractionDigits || 2);// maximumFractionDigits = Math.max(minimumFractionDigits, maximumFractionDigits);// return new Intl.NumberFormat('en-us', {// maximumFractionDigits: maximumFractionDigits || 2,// minimumFractionDigits: minimumFractionDigits || 2// }).format(number)// }const format = new Intl.NumberFormat('en-us');function format_with_Intl(number) {return format.format(number)}</script><script>function getData(count) {var data = new Array(count).fill(0).map(function(i) {var rd = Math.random();var r = rd * Math.pow(10, Math.trunc(Math.random() * 12));if (rd > 0.5) {r = ~~r;}return r})return data;}function test(data, fn, label) {var start = performance.now();for (var i = 0; i < data.length; i++) {fn(data[i]);}var time = performance.now() - start;message((fn.name || label) + ":" + time.toFixed(2) + "ms");}function executeTest() {var data = getData(+textCount.value);test(data, format_with_array);test(data, format_with_mod);test(data, format_with_substring);test(data, format_with_regex);test(data, format_with_toLocaleString);test(data, format_with_Intl);message("-------------------")}function message(msg) {var el = document.createElement("p");el.innerHTML = msg;messageEl.appendChild(el);}</script></body></html>