搞懂 JS 数据类型与 V8 执行原理,看完这篇就够了

95 阅读7分钟

引言

JavaScript 的变量存储与执行机制,是理解这门语言的关键。本文将从数据类型划分入手,详解基本类型与引用类型的存储差异,并结合 V8 引擎的执行过程,带你读懂 JS 代码运行的底层逻辑。

一、JavaScript 的数据类型:两类核心划分

JavaScript 里的所有数据,都被分为两大类:基本类型(原始类型)引用类型(复杂类型) ,它们的存储方式和特性完全不同。

二、什么是基本类型?

基本类型也叫原始类型,是 JavaScript 里最基础、最简单的数据形式,它们的特点是:

  • 数据本身是不可拆分的原子值(不能再拆成更小的数据单元)
  • 直接存储在栈内存中,存的是值本身

JavaScript 中的 7 种基本类型

1.string 字符串

  • 作用:用来表示文本内容

  • 特点:被单引号、双引号或反引号包裹

  • 例子:

let str = 'hello world'   //单引号
let str1 = ''hello world''  //双引号
let str2 = `hello world`   //反引号
let str3 = str + 'js'//第一种拼接方式
console.log(str3)  //输出结果:hello worldjs
let str4 = `${str} js`    //另一种拼接的方式,注意这里的引号是反引号,
console.log(str4)  //输出结果:hello world js

注意str3和str4的输出结果不一样,空格也是一个字符,字符串里面的每个元素都叫一个字符。

2.number 数字

  • 作用:表示所有数值,包括整数、小数、正负、科学计数法

  • 特点:不区分整数和浮点数,统一用 64 位浮点数存储

  • 例子:

let num = 123   //number类型
let num2 = num + 0.1  //
console.log(num2) //输出结果:123.1
console.log(num2.toString());  //number 类型转换为 string 类型。
console.log(num2 + '5');  //任何+字符串都变成字符串,输出结果为:123.14
>如何分别数字和字符串:数字呈现黄色,字符串呈现黑色 

```js
let n = 1
let m = '2m'
console.log(Number(m));  //字符串转为数字 Number类型
console.log(1 + Number(m));  //运行显示NaN数字类型
//NaN意思是not a number,非数字,但 NaN 这个输出值本身是一个 number 类型值。
console.log(9007199254740992 + 1);   //number 类型有一个上限,就是 2**53 ,超过这个值就不能增加了。

3.boolean 布尔值

  • 作用:表示逻辑上的 “真” 或 “假”,只有两个值

  • 特点:只有 truefalse 两个值,常用来做条件判断

  • 例子:

if (ture) {  // boolean  布尔类型   
console.log('小杰');  //判断成功,输出结果:小杰
}

if (false) {
console.log('小杰');  //判断失败,不输出结果
}

4.undefined(未定义)

  • 作用:表示变量声明了,但没有赋值的状态

  • 特点:不是主动赋值的,是 JS 自动给的默认值

  • 例子:

let a; // 只声明,没赋值 
console.log(a); // 输出结果:undefined
let u = undefined + ' 小杰' 
console.log(u)  //输出结果:undefined 小杰

5.null 空值

  • 作用:表示 “这里什么都没有”,是开发者主动给变量赋的空值

  • 特点:表示变量的指向为空,常用来清空对象

  • 例子:

let n = null  

💡 小补充:undefined 是 JS 给的默认状态,null 是开发者主动设置的空值。

6.bigint 大整数

  • 作用:用来表示超过 number 安全范围的超大整数

  • 特点:数字末尾加 n,解决 number 精度丢失问题

  • 例子:

let b = 123123123n   // bigInt 类型
let c = 9007199254740992n   /末尾带n

console.log(b + c); //输出结果:9007199377864115n

7.Symbol 符号

  • 作用:创建一个独一无二的值,用来避免对象属性名冲突

  • 特点:每次调用 Symbol() 生成的值都是唯一的,即使描述相同也不相等

  • 例子:

let s = Symbol(1)
let p = Symbol(1)

console.log(s == p);  //输出结果:false

💡 小补充:等号=的用法:
“=”一个等号为赋值号,把右边的值,存到左边的变量里,不是 “等于”,是存进去
“==” 两个等号为相等(不严格),只比较值是否一样,不看类型。值一样就行,类型不一样也会当成一样。
“===” 三个等号为严格相等(最常用、最安全),值 和 类型 必须完全一样

三、什么是引用类型?

引用类型也叫复杂类型,和基本类型完全相反:它不是一个简单的值,而是由多个值组合而成的复杂数据结构

你可以这么理解:

  • 基本类型 = 一个单独的东西(一个数字、一个名字)
  • 引用类型 = 一整套东西(一个人、一组列表、一个功能)

它最核心的特点有 3 个:

1.不直接存在栈里,而是存在堆内存中
2. 变量存的不是值,而是内存地址(引用)
3.赋值时不是复制内容,而是复制地址,所以会互相影响

最常见的 4 种引用类型

1.Array 数组

用来存放一组有序的数据,用 [] 表示。

例子:

var arr=['a','b',1,2] 
arr.push(3)  //向数组尾部添加一个值3 
arr.pop()  //移除数组尾部的一个值 
arr.unshift(0)  //向数组头部添加一个值0 
arr.shift() //向数组头部删掉一个值 
arr.splice(1,2)  //从下标1开始,删除2个
arr.splice(2)  //移除下标2之后的所有值 
arr.splice(2,0,true)  //从下标2开始,移除0个值即不移除,在这个位置加入ture
arr [2] = 10 // 将下标 2 的变量改为 10

2.Object 对象

用来描述一个事物的各种特征,用 {} 表示。

例子:

var obj = {
  name: 'hisoka',
  age: 20,
  like: {
    one: 'eat',
    two: {
      sports: ['魔术', '战斗'] 
    }
  }
}
obj.girlFriend = '小杰'  //添加一个对象
delete obj.girlFriend  //删除一个对象

3.Function 函数

可以被执行的代码块,用来实现某个功能。

例子:

var fn = function() {}

4.Date 时间日期

Date 是 JavaScript 里专门用来处理时间和日期引用类型。只要你想获取当前时间、年、月、日、时、分、秒,都要用 Date。

四、V8引擎是怎么执行 JavaScript 的?

我们写的 JS 代码,浏览器里的 V8 引擎会帮我们执行,它的执行过程和内存设计,藏着很多巧思,核心流程分三步:

  1. 创建调用栈(用来执行预编译的过程)
    调用栈就像一个 “先进后出” 的工作台,专门用来存放代码执行的上下文(比如当前执行到哪一行、变量的值),也是所有基本类型数据的 “直接存放地”。

  2. 在调用栈中,存入 xx 执行上下文
    每一段代码(比如全局代码、函数)开始执行时,V8 都会在调用栈中放入对应的执行上下文,让代码有独立的运行空间。

  3. 数据存储:栈存地址,堆存真实数据,效率拉满

执行代码,将原始值存入栈中:

  • 遇到基本类型数据时,直接把值本身存在调用栈里,读取和修改都很快。

  • 遇到引用类型数据时,不会把整个复杂数据直接塞进里,而是将值存入堆空间并生成一个引用地址,将引用地址存入

这种设计的好处非常明显:

  • 栈不需要设计得很大,保证了 V8 执行速度更快、效率更高;
  • 复杂数据都放在堆里,不会占用栈空间,有效避免栈溢出

💡 举个生活中的例子帮你理解

你可以把调用栈理解成 “你的口袋”,堆空间理解成 “你的储物柜”:

  • 基本类型就像硬币,直接装在口袋里,随时拿随时用;
  • 引用类型就像一个大行李箱,口袋装不下,只能放在储物柜里,口袋里只装储物柜的钥匙(也就是内存地址)。
  • 这样既不用把口袋做得巨大(影响携带效率),也能装下大行李箱,完美解决了空间和效率的矛盾。