oi 哥们 来解锁JavaScript数据类型与内存奥秘:2024年最新指南

433 阅读8分钟

前言:

在这个数字化飞速发展的时代,前端开发已经成为互联网技术的核心领域之一。JavaScript,作为前端开发的基石,其重要性不言而喻。ESNext标准的推进带来了更多的新特性,同时也对开发者的技能提出了更高的要求。,本文将带你深入探讨JavaScript的数据类型和内存管理机制,从基础到高级,从理论到实践,全面解析这些关键概念。

为什么你需要了解JavaScript数据类型和内存管理?

  1. 提升代码性能:理解数据类型和内存管理可以帮助你优化代码,减少不必要的内存占用,提高应用程序的性能。
  2. 避免常见错误:深入理解JavaScript的内部机制,可以帮助你避免一些常见的陷阱和错误,编写更加健壮的代码。
  3. 应对复杂问题:在处理复杂的应用场景时,对数据类型和内存管理的深刻理解将是你解决问题的利器。

了解了为什么我们需要知道JavaScript数据类型和内存管理,那么就开始今天的学习了!

JavaScript数据类型全解:

哥们,你知道JS有多少种数据类型吗?

JavaScript数据类型:高手与高高手的区别

  • 高手会讲8种

    • Number:数字,包括整数和浮点数。
    • BigInt:大整数,ES10引入,解决大数精度问题。
    • String:字符串,用来存储文本信息。
    • Boolean:布尔值,表示真(true)或假(false)。
    • Null:空值,表示一个空对象引用。
    • Undefined:未定义,表示一个声明但未赋值的变量。
    • Symbol:唯一标识符,ES6引入,每次创建都是唯一的。
    • Object:复杂数据类型,包括普通对象、数组、函数等。
  • 高高手会讲7种

    • 其中的Numeric:包括NumberBigInt,因为它们都是用来表示数值的。
    • 其他不变

这样如果当面试官问到类似的问题,你如此回答,哇哦,他一听,就知道哥们你是十年老JS了。 那么我们来详细的讲解一下数据类型里面的知识点吧!

我们来讲解的第一个是null这种数据类型:

null是什么?在JavaScript的世界里,null是一个特别的存在。它不仅仅是一个简单的值,更是编程世界中的一种哲学思考。它表示空值 表示一个空值或不存在的对象,是一个可以赋给变量的特殊值。它还有个很牛的作用,就是可以显示回收内存,这个知识点和底层的内存机制有关,所有我们得结合实例来看:

实例1:
let a=null; // 栈内存
console.log(a); 

// 堆内存
let largeObject={
    data: new Array(1000000).fill('a')
}
largeObject=null; // 释放堆中的内存 垃圾回收 // 哲学 // 不存在的对象

通过这个例子,largeObject中的内存通过Null进行了回收,这是其中一个作用,那么我们可以深入的去看看JS的底层内存是如何存储的。

内存分配机制揭秘

在JavaScript中,内存分配主要分为两种:栈(Stack)和堆(Heap)。

  • 栈(Stack) :用于存储基本数据类型。栈内存分配速度快,但容量有限。当一个基本类型的数据被创建时,它的值直接存储在栈中。
  • 堆(Heap) :用于存储复杂数据类型。堆内存分配较慢,但容量较大,适合存储大型对象。当一个对象被创建时,它实际上是在堆中创建,而在栈中只存储了一个指向该对象的引用。

这样我们对JS中的栈和堆有了一个简单的了解,那么来看看实例:

实例2:
let obj={
    name:"张三",
    job:"web",
    company:"baidu"
}
obj.hometown="北京"
// 引用式
let obj2=obj;
let a=1;
// 拷贝式
let b=a;
b=3;
obj2.name="李四"
console.log(a,b);
console.log(obj,obj2);

看了上面的代码,大家不妨猜猜结果是什么?

公布答案:1 3 { name: '李四', job: 'web', company: 'baidu', hometown: '北京' } { name: '李四', job: 'web', company: 'baidu', hometown: '北京' }

问题:为什么obj1的输出和obj2的输出一样?

讲解

  1. 当我们赋值给简单类型时,基本数据直接被压入栈中,加以保存,如上面的 a=1,b=a,,简单数据类型拷贝。
  2. 当我们赋值给复杂类型时,是将堆内存地址放在栈内存中,如果出现obj2=obj1这种情况时并不是简单的拷贝,而是进行引用,将obj2和obj1指向同一个堆内存,这样就说明了为什么,为什么obj1name属性也随着obj2name属性改成一样了,由于 obj2 和 obj1 指向同一个对象,所以这个修改也会反映在 obj1 上。
  • 想必大家有疑问,为什么不直接复杂数据类型放入栈中呢?
    • 动态内存管理:复杂数据类型的大小往往不是固定的,或者是在运行时才能确定。例如,动态数组、链表、树等数据结构的大小可能随着程序的执行而变化。这些类型的数据更适合存储在堆中,因为堆提供了更灵活的内存分配方式,可以按需申请和释放内存。

    • 避免栈溢出:栈的大小是有限的,通常由操作系统设定。如果在栈上分配了过大的数据结构,可能会导致栈溢出,从而引发程序崩溃。将大型或复杂的对象放在堆中可以避免这种情况发生,因为堆的可用空间通常比栈大得多。

    • 共享与传递:堆上的对象可以更容易地在不同的函数或线程之间共享。由于堆上的对象不受局部作用域限制,多个指针或引用可以指向同一个堆对象,这在多线程编程和跨模块通信中尤为重要。

图解

lQLPJwWHBZWzPtXNAW_NA4awXiZVHl-sKWgHJT4wOe9uAA_902_367.png

lQLPJwolXX0xi1XNAxbNA6GwcTMvgUKnqsAHJT4pL__-AA_929_790.png 当你理解好了JS的底层内存机制,本文的阅读就已经成功一半啦!

第二位向我们走来的是你独一无二的白月光 Symbol类型:

独一无二的标签,就如你少年时,窗外的白月光般, 有着像函数的外观,是一个简单的数据类型,进入实例:

// js 设计哲学 你!=你
console.log(Symbol());
console.log(Symbol('a')===Symbol('a'));

结果: Symbol() false

讲解: 就像是今天的你和昨天的你也是不一样的!symbol代表的就是独一无二的。

第三位是bigint:

这次先看案例:

  let a=0.1;
  let b=0.2;
  console.log(a+b);
 let num1= 999999999999999999999999;
 let num2= 123456789123834348938394;
 console.log(num1+num2);

结果: 1:0.30000000000000004 2:1.1234567891238343e+24

JS就像是一把双刃剑,是一个有很强大表现力的语言,但它并不适合运算,就得请我们的bigint来解决第二个因运算太大的麻烦了,只需要在后面加个n,它就变成了bigint类。

let num1= 999999999999999999999999n;
let num2= 123456789123834348938394n; 
console.log(num1+num2);// 结果为:1123456789123834348938393n

其他数据类型就留给你们去好好钻研一下。既然有那么多数据类型,那么我们怎么去判断某个数据的数据类型呢?

实战技巧

类型判断

typeof操作符可以快速判断基本数据类型,但对nullobject的判断有局限,废话少说进入实例:


// typeof js 类型 运算符
// console.log(typeof a,typeof(a));
// The Good Parts 什么好的,什么是坏的
let a=1;
console.log(typeof a,"1");

console.log(typeof "hellow");
console.log(typeof 12n);
console.log(typeof true);
console.log(typeof Symbol());
console.log(typeof undefined);
console.log(typeof null);// 不太行
console.log(typeof function(){});

结果:

  • number 1
  • string
  • bigint
  • boolean
  • symbol
  • undefined
  • object
  • function

讲解: 可以发现除了nullobject的判断,其他都是对的

为什么会这样?

这个问题源于JavaScript的早期实现。在1995年,JavaScript的设计者Brendan Eich在设计语言时,由于时间和资源的限制,将 null 的类型错误地定义为了 object。具体来说,null 被设计为一个表示“空对象引用”的值,而不是一个独立的类型。

这种设计在当时是为了与C++等语言保持一致,因为在C++中,nullptrNULL 通常被定义为 (void*)0,即一个空指针。因此,null 在JavaScript中被设计为一个特殊的对象引用,而不是一个独立的基本类型。 虽然这是一个历史遗留问题,但为了保持向后兼容,JavaScript一直没有修复这个问题。这意味着在现代JavaScript中,typeof null 仍然会返回 "object"

简单来讲就是一个BUG。

结语:

通过本文的学习,你不仅掌握了JavaScript的数据类型和内存管理机制,还了解了这些概念背后的原理和历史背景。这些知识将帮助你在日常开发中编写更高效、更健壮的代码。希望本文对你有所帮助,如果你有任何疑问或建议,欢迎在评论区留言交流。祝你在JavaScript的开发之旅中越走越远,越走越顺!