JavaScript 的基本数据类型

149 阅读9分钟

JavaScript 的基本数据类型

mountain-7011121_1280.webp JavaScript 的数据类型分为基本数据类型和引用数据类型两大类,共 7 种,这些类型共同构建起JavaScript 的数据体系大厦。

基本数据类型

基本数据类型的值直接存储在栈内存中,变量直接持有值本身而非引用。当我们对基本数据类型进行赋值传递操作时,采用的是拷贝式传值,这意味着新变量会获得原变量值的一份独立拷贝,对新变量的修改不会影响到原变量。例如:

let a = 1; // 栈内存中为a分配独立存储空间来存储 1 
let b = a; // 从栈内存复制 赋值给b时创建独立副本
b = 2;
consloe.log(a); // 1  输出为1 说明a没有被修改,证明了其内存的独立性

ECMA262标准定义了基本数据类型有7种,分别为**NumberStringboolenundefinenullSymbolbigInt**,其中NumberbigInt可以归为Numeric,所以有时也会说有6种基本数据类型。

下面是对各基本数据类型的介绍:

  1. 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 + 12^53 - 1**,其最大值和最小值分别可以用Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER来表示

      console.log(Number.MAX_SAFE_INTEGER) // 9007199254740991
      console.log(Number.MIN_SAFE_INTEGER) // -9007199254740991
      
    • Number数据类型中存在特殊值,即Infinity (正无穷)与-Infinity(负无穷),以及NaNNaN表示的是不是数值的数值类型

      就是说它类型是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.10.2 在二进制中无法精确表示,在计算时会产生微小误差。

      0.1+0.20.3000000000000000444089209850062616169452667236328125
      0.30.299999999999999988897769753748434595763683319091796875
      

      要解决该问题可以在计算时使用整数运算:

      consloe.log((0.1 * 10 + 0.2 * 10)/ 10 === 0.3) // true
      
  2. 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) // bigint
    

    BigInt 可以在大数计算、大数比较、高精度计算等场景发挥作用,但需要注意到的是:

    • 不能与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 
      
    • 当作为条件判断时 BigIntnumber相似,只要不为0n判断结果就为true

      
      if(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] 
      
  3. 字符串

    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字节),字符串有多种字节编码
  4. 布尔值

    布尔值只有两个成员 ——truefalse(小写)

    在条件判断或Boolean()时会转换为false的值有:0、空字符串、undefinedNaNnull

    console.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
    
  5. undfinednull

    Undefined表示变量已声明但未赋值,或者变量已被声明但尚未初始化(初始化时没有赋值的默认状态)。

    当我们声明一个变量let a;,此时a的值就是undefined;或者访问对象不存在的属性时返回 undefined;函数没有明确返回值时默认返回 undefined

    let 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()) // undefined
    

    undefined是全局对象上的属性,不是关键字/字面量,所以它可以被用作变量名

    window.undefined === undefined // true
    console.log(global.undefined) // undefined
    
    // 作变量名 
    let undefined = 100
    console.log(undefined) // 100
    

    null是一个字面量(在代码中表示固定值,可以直接使用),表示空对象指针用于表示一个对象不存在或已被清除。例如,当我们想要初始化一个变量,表明它目前没有指向任何对象时,可以将其赋值为null,即let obj = null;

    
    const btn = document.getElementById('btn')
    console.log(btn) // 若id不存在则返回null
    

    null 是字面量:不是任何对象的属性,不可变,是语言关键字,不能被删除

  6. Symbol

    Symbol 是 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,不是 description

      const 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 不在注册表中