底层机制
js
var l = { x: 10 }
var g = l
l.y = l = { x: 200 }
console.log(l.y)
console.log(g)1 相关名词
分析代码执行题时会用到的名词
- JS 执行平台
- 不同的浏览器
- Nodejs
- webview
- 不论在哪一种平台上执行 JS 都需要具备代码执行的环境
- 执行环境栈
- 不论何种编程语言编写的代码,最终执行都是发生在内存中
- 每当浏览器加载界面时就会从计算机内存中申请一片空间,称之为执行环境栈
- ECS(Execute context stack)
- 它就像一个大容器,将来所有的代码执行都会在这个空间内完成
- 执行上下文
- 一个 JS文件会包含多行代码,不同行又构成了分支、循环、函数、对象等代码块
- 如果将多个代码块中的代码都直接放入到执行环境栈中执行,那么很容易就会出现干扰和语法冲突
- 每个代码块都有自己的执行上下文,在上下文中保存了当前段代码执行时所需的数据
- 执行上下文使用EC?(Execution Context) 表示
- 进栈进执行
- 执行环境栈是一个先进后出的栈结构
- 代码运行时会产生不同的执行上下文
- 不同的执行上下文进栈执行,代码执行结束后,决定是否出栈
- EC(G)
- Execution context global 全局执行上下文,浏览器加载界面时默认创建
- VO(G)
- variable object 全局变量对象,用于存放全局上下文当中声明定义的变量
- GO
- global object 全局对象,它和 VO 并不是一个东西,在浏览器平台下我们可以看做是 window
- 做为一个对象,它同样占据空间,浏览器加载界面时就会创建,在它内部保存了许多JS可以直接使用的API
- 例如
setTimeoutsetIntervalJSON等 - 为了方便使用上述的 API, 在 VO(G) 当中就创建了一个 window 属性指向当前的空间
- 声明:采用具体的关键字声明一个变量 var let const function
var name - 定义:定义就是给某一个变量执行赋值
name = '跳跳'
2 堆栈中的基本值
js
var l = 100
var g = l
g = 101
console.log(l)
/*
01 浏览器会开启一个线程专门用于执行 JS 代码,同时申请空间做为执行环境栈
02 浏览器加载界面的时候会创建 EC(G) 全局执行上下文,然后代码进栈执行
03 全局执行上下文当中会存在VO(G),用于保存当前执行上下文中的数据
04 代码执行之前会存在变量提升,var 声明的变量在提升阶段只声明就定义
*/总结 1 浏览器加载界面的时候默认会创建执行环境栈、全局执行上下文、GO 2 EC(G) 内部会有 VO(G) 专门用于存放当前上下文当中的数据 3 EC(G) 上下文会在浏览器关闭之后执行出栈,释放掉相当的空间 4 基本类型值保存在栈空间当中 5 作用域链查找,代码运行时使用到了某个变量,首先会在当前上下文中查找,如果没有则继续向上,直到GO
3 堆栈中的引用类型
js
var l = { x: 17 }
var g = l
g['y'] = 100
console.log(l.y)
-------------------------------------------------
var l = { x: 10 }
var g = l
g = { y: 100 }
console.log(l.x)总结 01 基本数据类型(原始值)存放在栈内存当中,引用类型存放在堆内存空间中 02 每个堆内存都会有一个 16进制的内存地址 03 在栈区中存放的就是能找到某个堆内存的16进制地址
4 堆栈中的函数
函数本身也是对象, 对于它的分析一般分为函数的创建和函数的执行
js
var lg = [88, 100]
function foo(obj) {
obj[0] = 100
obj = [100]
obj[1] = 200
console.log(obj)
}
foo(lg)
console.log(lg)4.1 函数的创建
- 变量提升阶段对于函数的来说既声明又定义
- 函数的创建和变量的提升类似,可以将函数名看做是一个变量名,不同的就是包含了声明 + 定义
- 函数也是一个对象,因此它同样在堆中存储,然后将内存地址存放在栈区
- 对于函数来说,声明和定义都是发生在提升阶段,因此代码执行时看到了function foo(){} 这种代码后一般是不执行操作
- 函数在创建的时候就确定了作用域,也就是当前的执行上下文
- 在创建函数的时候它的内存当中存放的是字符串形式的函数体
4.2 函数的执行
- 函数执行的目的就是为了将内存当中存储的字符串形式的代码真正的运行起来
- 代码运行时需要保证当前代码段与其它代上下文当中的代码段相互隔离,所以函数每次执行都会生成一个全新的执行上下文
- 执行步骤
- 确定作用域链
- 确定函数体中的 this
- 初始化 arguments
- 形参赋值
- 变量提升
- 函数代码执行
函数执行时的形参赋值就相当于在 AO(G) 当中新增属性