JavaScript 的基本数据类型
JavaScript 的数据类型分为基本数据类型和引用数据类型两大类,共 7 种,这些类型共同构建起JavaScript 的数据体系大厦。
基本数据类型
基本数据类型的值直接存储在栈内存中,变量直接持有值本身而非引用。当我们对基本数据类型进行赋值传递操作时,采用的是拷贝式传值,这意味着新变量会获得原变量值的一份独立拷贝,对新变量的修改不会影响到原变量。例如:
let a = 1; // 栈内存中为a分配独立存储空间来存储 1
let b = a; // 从栈内存复制 赋值给b时创建独立副本
b = 2;
consloe.log(a); // 1 输出为1 说明a没有被修改,证明了其内存的独立性
ECMA262标准定义了基本数据类型有7种,分别为**Number、String、boolen、undefine、null、Symbol、bigInt**,其中Number与bigInt可以归为Numeric,所以有时也会说有6种基本数据类型。
下面是对各基本数据类型的介绍:
-
Number数值类型JavaScript 不像其他语言那样区分整数和浮点数,它只有一种数值类型
Number,并采用 IEEE 754 双精度浮点数格式(64位)存储-
所有数字都是
Number类型,包括整数和浮点数let num = 86 console.log(typeof num) // number let decimal = 86.66 console.log(typeof decimal) // number -
Number数值范围为**-2^53 + 1到2^53 - 1**,其最大值和最小值分别可以用Number.MAX_SAFE_INTEGER与Number.MIN_SAFE_INTEGER来表示console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991 console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991 -
Number数据类型中存在特殊值,即Infinity(正无穷)与-Infinity(负无穷),以及NaN。NaN表示的是不是数值的数值类型就是说它类型是
Number,但是它不是数值(无效数学运算)。例如0 / 0就是NaN判断是否是
NaN可以使用Number.isNaN(),并且**NaN=== NaN为false**// 结果为NaN的运算 let res = 0 / 0 console.log(res) // NaN console.log(Number('abc')) // NaN console.log(typeof res) // number console.log(Number.isNaN(res)) // true console.log(NaN === NaN) // false -
不同进制表示的方式
// 二进制 以0b前缀表示 console.log(0b1010) // 八进制 以0o前缀表示 console.log(0o755) // 十六进制 以0x前缀表示 console.log(0xFF) -
浮点数计算的精度问题 0.1 + 0.2 = 0.3 ?
64位双精度浮点数 存储:1位符号位表示正负;11为指数位(表示范围,科学计数法的指数部分,例如10^3中的3);52位尾数位(有效数字)
由于存储机制导致
0.1和0.2在二进制中无法精确表示,在计算时会产生微小误差。0.1+0.2≈0.3000000000000000444089209850062616169452667236328125 0.3≈0.299999999999999988897769753748434595763683319091796875要解决该问题可以在计算时使用整数运算:
consloe.log((0.1 * 10 + 0.2 * 10)/ 10 === 0.3) // true
-
-
BigInt**在整数字面量后加
n或者使用BigInt()**可以都可以创建BigInt,BigInt它解决了传统 Number 类型无法精确表示大整数的问题。例如:let num1 = 9999999999999999 console.log(num1); // 结果为 10000000000000000 console.log(typeof num1) // number let num2 = 9999999999999999n console.log(num2) // 9999999999999999n console.log(typeof num2) // bigintBigInt可以在大数计算、大数比较、高精度计算等场景发挥作用,但需要注意到的是:-
不能与Number混合运算(
BigInt不能隐式转换,引擎不会自动将 BigInt 转换为 Number)也不能使用Math对象中的方法(因为这些对象方法需要接受的参数是Number数值类型的数字)。let n1 = 11111111111n let n2 = 2 console.log(n1 + n2) // TypeError: 不能混合使用BigInt和其他类型,需要显式转换。 console.log(Math.max(1n, 2)); //TypeError: Cannot convert a BigInt value to a number -
当作为条件判断时
BigInt与number相似,只要不为0n判断结果就为trueif(0n){ // false console.log('0n') } if(1n){ // true console.log('1n') } -
在对
bigInt进行排序时使用sort要注意,第一点数组元素必须全为bigInt类型的,第二点sort要求的返回结果必须是number类型(-1/0/1),这里存在隐式转换。//当数组元素都为`BigInt`时可以进行sort const arr = [1n, 2n, 3n]; arr.sort((a, b) => { return b - a; // Array.sort() 的比较函数要求返回 `Number` 类型(-1/0/1),而 BigInt 的减法运算会返回 `BigInt` 类型,所以这里存在隐式转换。 可以写成 arr.sort((a, b) => a > b ? -1 : 1) }); console.log(arr); // 预期[3n, 2n, 1n]
-
-
字符串
js中的字符串是表示文本数据的基本数据类型,用于存储和操作文本内容。字符串是不可变的,一旦创建了其内容就不能被改变,所以所有字符串方法都返回新字符串。字符串可以使用单引号也克使用双引号来表示,并且在ES6之后引入了模板字符串,如下:
let name = '张三' let age = 18 let sex = "男" let message = `My name is ${name}, I am ${age} years old, and I am a ${sex}`-
如何证明字符串是不可变的
let str = "AiKun" str[0] = 'H' console.log(str) // AiKun let str2 = str.toUpperCase() console.log(str) // AiKun console.log(str2) // AIKUN -
为什么字符是不可变的
这是由于字符串字符串常被用作对象键,其不可变性保证了哈希值的稳定性,可以防止意外修改,并保证安全地进行字符串共享
-
数组索引与字符串索引
javaScript字符串的底层实现不是简单的数组,而是一种特殊的数据结构(V8引擎)。这种结构:- 使用连续的内存块进行存储:字符串内容存储在连续的内存中
- 可以通过索引进行访问,时间复杂度为O(1)
- 编码自适应:可以自动选择 ASCII(1字节/字符)或 UTF-16(2字节/字符)编码
与数组的区别:
- 数组元素可以修改,字符串内容不可改变
- 字符串方法返回新的字符串,而数组方法可能是修改原数组
- 数组元素所占空间大小固定(8字节),字符串有多种字节编码
-
-
布尔值
布尔值只有两个成员 ——
true和false(小写)在条件判断或
Boolean()时会转换为false的值有:0、空字符串、undefined、NaN、nullconsole.log(Boolean(0)) // false console.log(Boolean('')) // false console.log(Boolean(undefined)) // false console.log(Boolean(null)) // false console.log(Boolean(NaN)) // false转换为
true的值:非零数字、非空字符串、所有对象(包括{ }与[ ])console.log(Boolean(-1)) // true console.log(Boolean('s')) // true console.log(Boolean([])) // true console.log(Boolean({})) // true -
undfined与nullUndefined表示变量已声明但未赋值,或者变量已被声明但尚未初始化(初始化时没有赋值的默认状态)。当我们声明一个变量
let a;,此时a的值就是undefined;或者访问对象不存在的属性时返回undefined;函数没有明确返回值时默认返回undefinedlet a console.log(typeof a) // undefined const obj = { a: 1, b: 2 } console.log(obj.name) // undefined console.log(obj['name']) // undefined function fun() { console.log('fun'); } console.log(fun()) // undefinedundefined是全局对象上的属性,不是关键字/字面量,所以它可以被用作变量名window.undefined === undefined // true console.log(global.undefined) // undefined // 作变量名 let undefined = 100 console.log(undefined) // 100null是一个字面量(在代码中表示固定值,可以直接使用),表示空对象指针,用于表示一个对象不存在或已被清除。例如,当我们想要初始化一个变量,表明它目前没有指向任何对象时,可以将其赋值为null,即let obj = null;。const btn = document.getElementById('btn') console.log(btn) // 若id不存在则返回nullnull是字面量:不是任何对象的属性,不可变,是语言关键字,不能被删除 -
SymbolSymbol 是 ECMAScript 6 (ES2015) 引入的一种新的基本数据类型,用于创建唯一的标识符。每个 Symbol 在创建时都会获得一个内部唯一的标识(引擎实现细节)。
它就像是给数据贴上了一个独一 无二的标签,
Symbol可以用于对象属性名冲突的场景。比如,在一个大型项目中,多个模块可能会向同一个对象添加属性,为了避免属性名冲突,就可以使用 Symbol 作为属性名。
const sy = Symbol('sy') const myObject = { [sy]: "这是一个独特的属性" , // 这样就确保了属性名的唯一性。 sy : "other" // 不冲突 } console.log(myObject[sy]) // '这是一个独特的属性' console.log(myObject['sy']) // other-
每个 Symbol 值都是唯一的,相同描述的 Symbol 也不相等,无法通过任何方式创建相同的Symbol,且Symbol 值创建后不能被修改
// 唯一性 const sy1 = Symbol('name') const sy2 = Symbol('name') console.log(sy1===sy2) // false // 不可修改 let sy = Symbol('Id') sy.description = 'name' // 严格模式下会报错 console.log(sy.description) // Id -
Symbol.for(key)全局注册的Symbol,与Symbol()Symbol.for(key)全局注册的Symbol,与Symbol()区别:-
Symbol()创建的 Symbol 是完全独立的,不会与全局注册表有任何关联。const sym2 = Symbol('id') // 传入的id只是description 不是key console.log(Symbol.keyFor(sym2)) // undefined 在注册表中找不到 -
Symbol.for("id")会在全局注册表中存储 Symbol 并关联 key(传入的字符串)。 -
Symbol 全局注册表:是 JavaScript 引擎维护的一个跨代码域、跨模块的 Symbol 共享机制,它允许开发者通过字符串 key 创建或获取全局唯一的 Symbol。
Symbol.for(key)表示检查全局Symbol注册表中是否已存在以该key注册的 Symbol,若存在则返回该注册表中的Symbol,若不存则以该key创建Symbol,并自动将其 description 设为 key,然后注册到全局表中。这里的key指的是传入的字符串参数,并不是指
Symbol的描述(description)const sym1 = Symbol.for('id') const sym2 = Symbol('id') const sym3 = Symbol.for('id') console.log(sym1===sym2) // false console.log(sym1===sym3) // true通过
Symbol.keyFor()可以获取Symbol注册的key,不是 descriptionconst sym1 = Symbol.for('id') const sym2 = Symbol('id') const re = Symbol.keyFor(sym1) console.log(typeof re) // string console.log(Symbol.keyFor(sym1)) // id console.log(Symbol.keyFor(sym2)) // undefined 使用普通方式创建的Symbol 不在注册表中 -
-