关于栈

编译
词法解析:这个过程会将由字符组成的字符串分解成有意义的代码块(词法单元)。
语法分析:这个过程将词法单元流转换成一个由元素逐级嵌套所组成的代表了程序语法的树(抽象语法树 => AST)。
代码生成:将AST转换为可执行代码的过程被称为代码生成。
引擎编译执行代码
然后将构建出的代码交给引擎(V8),这个时候可能会遇到变量提升、作用域和作用域链/闭包、变量对象、堆栈内存、GO/VO/AO/EC/ECStack、…。

引擎在编译执行代码的过程中,首先会创建一个执行栈,也就是栈内存(ECStack => 执行环境栈),然后执行代码。

在代码执行会创建EC(执行上下文),执行上下文分为全局执行上下文(EC(G))和函数执行上下文(EC(…)),其中函数的执行上下文是私有的。

创建执行上下文的过程中,可能会创建:

GO(Global Object):全局对象 浏览器端,会把GO赋值给window
VO(Varible Object):变量对象,存储当前上下文中的变量。
AO(Active Object):活动对象
然后将进栈执行,创建好的上下文将压缩到栈中执行,执行后一些没用的上下文将出栈,有用的上下文会压缩到栈底(闭包)。栈底永远是全局执行上下文,栈顶则永远是当前执行上下文。

下面的一张图表达了整个流程

变量赋值的三步操作
第一步,创建变量,这个过程叫做声明(declare)。

第二步,创建值。基本类型值会直接在栈中创建和存储;由于引用类型值是复杂的结构,所以需开辟一个存储对象中键值对(存储函数中代码)的内存空间,这个内存就是堆内存,所有的堆内存都有可被后续查找的16进制地址,后续关联赋值时,是把堆内存地址给予变量操作。

最后一步,将变量和值关联,这个过程叫做定义(defined)。这里,如果值只经过了声明,而没有进行赋值操作,这个值就是未定义(undefined)。

一道题理解这个过程
我将以画图的形式展示
// 1.
let a = 12;
let b = a;
b = 13;
console.log(a);

// 2.
let a = {n:12};
let b = a;
b[‘n’] = 13;
console.log(a.n);

// 3.
let a = {n:12};
let b = a;
b = {n:13};
console.log(a.n);
第一问
创建执行栈,形成全局执行上下文,并且创建GO,进入栈中执行代码

基本类型直接在栈中创建和存储

所以本问最终输出的a值为12。

第二问
创建执行栈,形成全局执行上下文,并且创建GO,进入栈中执行代码

引用类型值比较复杂,将创建堆内存

所以本问最终输出的a.n为13

第三问
创建执行栈,形成全局执行上下文,并且创建GO,进入栈中执行代码

引用类型值比较复杂,将创建堆内存

所以本问最终输出的a.n的值为12。

几道面试题让你更深次理解浏览器堆栈内存的底层处理机制
第一个题
let a = {
n: 10
};
let b = a;
b.m = b = {
n: 20
};
console.log(a);
console.log(b);
创建执行栈,形成全局执行上下文,并且创建GO,进入栈中执行代码

引用类型值比较复杂,将创建堆内存

所以最终输出的a为{n: 10, m: {n: 20}};b为{n: 20}

第二个题:
let x = [12, 23];
function fn(y) {
y[0] = 100;
y = [100];
y[1] = 200;
console.log(y);
}
fn(x);
console.log(x);
首先会创建ECStack,形成全局执行上下文,创建VO(变量对象), 然后进入栈中执行代码

变量赋值

接下来会执行fn(x)函数,函数执行会形成一个全新的执行上下文,会产生AO。上面说过,栈顶永远是当前执行上下文,栈底是全局执行上下文,所以函数执行,函数执行上下文将进栈,会将全局执行上下文压入栈底。

然后进行代码的执行操作,执行后会出栈

继续执行,打印出x,经过上述分析:

答案是:[100, 200] [100, 23]

第三个题
var x = 10;
~ function (x) {
console.log(x);
x = x || 20 && 30 || 40;
console.log(x);
}();
console.log(x);

所以,最终的结果为:undefined 30 10

第四题
let x = [1, 2],
y = [3, 4];
~ function (x) {
x.push(‘A’);
x = x.slice(0);
x.push(‘B’);
x = y;
x.push(‘C’);
console.log(x, y);
}(x);
console.log(x, y);

所以本题最终的输出结果为[3, 4, ‘C’] [3, 4, ‘C’] [1, 2, ‘A’] [3, 4, ‘C’]。

垃圾回收机制
浏览器的Javascript具有自动垃圾回收机制(GC:Garbage Collecation),垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。
标记清除
在js中,最常用的垃圾回收机制是标记清除:当变量进入执行环境时,被标记为“进入环境”,当变量离开执行环境时,会被标记为“离开环境”。垃圾回收器会销毁那些带标记的值并回收它们所占用的内存空间。

function demo() {
var a = 1; // 标记"进入环境"
var b = 2; // 标记"进入环境"
}

demo(); // 函数执行完毕,a和b标记为"离开环境"
引用计数
浏览器会跟踪记录值的引用次数,每多引用一次,引用次数就会加1,取消占用,引用次数就会减1,当引用次数为0时,浏览器会进行垃圾回收。

下面的例子说明a引用次数的变化

function demo() {
var a = {}; // +1
var b = a; // +1 => 2
b = null; // -1 => 1
a = null; // -1 => 0 ====> 此时会回收
}
内存泄漏
内存泄露是指程序中的某些函数或者任务执行完毕之后,本该释放的内存空间,由于各种原因,没有被释放,导致程序越来越耗内存,最终可能引发程序崩溃等各种严重后果。
在 JS 中,常见的内存泄露主要有 4 种

全局变量
一个例子直接说明:

var obj = null;
function foo(){
obj = { name:“小红” };
}
foo();
上述代码中,obj是一个全局变量,这样,所有和obj作用域同层级的函数都可以访问到obj对象,所以obj对象不会被回收。

闭包

### 关于蓝桥杯竞赛中的数据结构 在蓝桥杯青少组比赛中,作为一种重要的线性数据结构,在编程题部分经常被考察[^1]。这类题目通常会测试参赛者对于基本操作的理解以及应用能力。 #### 的基本概念及其特性 是一种遵循后进先出原则的数据容器,只允许在一端执行插入和删除动作。其核心功能包括但不限于压入(push)、弹出(pop),以及获取顶部元素(top)等方法。 #### 可能涉及的具体考点 - **基础算法实现** 使用Python编写简单的类来模拟实际应用场景下的行为模式。 ```python class Stack: def __init__(self): self.items = [] def is_empty(self): return not bool(self.items) def push(self, item): self.items.append(item) def pop(self): if not self.is_empty(): return self.items.pop() def peek(self): if not self.is_empty(): return self.items[-1] def size(self): return len(self.items) ``` - **经典问题求解** 如括号匹配验证、表达式计算等问题都可借助来进行高效处理。例如给定一系列左/右圆括号字符串判断它们能否正确闭合就是一个典型例子。 - **复杂度分析** 对基于的操作时间复杂性和空间消耗有清晰认识也是考核重点之一;比如上述代码片段中各成员函数的时间效率均为O(1)。 #### 实际案例分享 过往真题里曾出现过利用两个队列构建一个具有标准接口的新这样的开放性命题形式,这不仅考验选手对理论知识掌握程度更注重灵活运用技巧解决问题的能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值