# 执行环境栈和执行上下文
JS代码运行都需要在一个环境中,大概包括以下几种:
1、浏览器(引擎)
2、node(基于v8渲染js)
3、webview(原生app v8引擎)
...
来了解下面这些名词都代表了什么!
执行环境栈ECStack(Execution Context Stack):浏览器想要运行JS代码, 需要提供一个供代码执行的环境, 该环境即为执行环境栈, 又称栈内存(从内存中分配出来的一块内存)。
执行上下文EC(Execution Context):在编程语言中为了区分全局和函数执行所处的不同作用域,目的是为了区分每个词法作用域下代码的独立性,形成执行上下文(代码执行所在的词法作用域)进栈执行,执行完出栈。
在每一个上下文代码执行的时候,都可能会创建变量。所以在每一个上下文中(无论是全局还是私有),都会有一个存储变量的空间:分别为:VO(Varibale Object)和AO(Active Object)。
变量对象:存放当前上下文中的变量,只不过全局上下文称为VO(G), 私有上下文称为AO(G),但也是变量对象。
全局对象:**GO(Global object)**跟VO/AO没有关系,浏览器把所有后期需要提供JS调取使用的属性和方法(内置)都放在GO对象中,并且在全局中创建一个window变量指向它。全局对象方法有setInterval、eval、JSON... . 等。
这么看,我们可以把执行环境栈理解为一个菜市场,那么市场中每个店面都是一个私有上下文,互相独立,各家卖各家的蔬菜水果。每家店中的蔬果都可以当作是AO中的对象。市场中的管理部门以及停车场都可以当作是全局执行上下文,白天卖菜就要进市场开门,代表了进栈执行,等到晚上关门,代表执行完出栈。个人觉得可以这样理解。
附上一张图供大家理解:
# 堆栈内存面试题
# 根据例题讲解堆栈内存的底层机制
值类型是基本数据类型时:
[例题1]
var a = 12;
var b = a;
b = 13;
console. log(a); 12
创建变量且赋值的三步操作:
1、创建一个值:
基本类型值直接存储在栈内存中
引用数据类型值需要先开辟一个堆内存,把内存存储进去后,把堆内存地址存放到栈内存中供变量调用。
2、声明变量:
创建一个变量存储在变量对象中
3、定义变量
让变量和值进行关联(指针指向的过程)
值类型是引用数据类型时:
[例题2]
var a = {n:12};
var b = a;
b['n'] = 13;
console. log(a. n); 13
解题思路:
# 对象数据类型
对象数据类型:由零到多组键值对(属性名和属性值)组成的
属性名的类型
说法1:属性名类型只能是字符串或者Symbol
说法2:属性名类型可以是任何基本类型值,处理中可以和字符串互通(个人更认同说法2)
但是属性名绝对不能是引用数据类型,如果设置引用类型,最后也是转换为字符串处理的
let sy = Symbol('AA');
let x = {
0:
};
let obj = {
0: 12,
true: 'xxx',
null: 20
};
obj[sy] = '珠峰';
obj[x] = 100; // obj['[object Object]'] = 100 会把对象变为字符串作为属性名
for (let key in obj) {
// for in 遍历中获取的属性名都会变为字符串
// 并且无法迭代到属性名是Symbol类型的属性
console. log(key, tyoeof key)
// 0 string
// true string
// null string
// [object Object] string
}
# obj[x]和obj['x']的区别
let x = 20;
let obj = {
x: 100
};
区别
属性名肯定是一个值
obj[x] 把x变量存储的值当做属性名,获取对象的属性值 => obj[20] => undefined
obj['x'] 获取属性名为x的属性值 => 100 <=> obj. x 属性名为x
//example 1 属性名是同一个时,后一个会覆盖前一个
var a={}, b='0', c=0;
a[b]='小红'; // a['0'] = '小红'
a[c]='小明'; // a[0] = '小明'
console. log(a[b]); // '小明'
---------------------
//example 2 Symbol 是唯一值 b !== c
var a={}, b=Symbol('1'), c=Symbol('1');
a[b]='小红';
a[c]='小明';
console. log(a[b]); // '小红'
---------------------
//example 3 属性名是同一个时,后一个会覆盖前一个
var a={}, b={n:'1'}, c={m:'2'};
a[b]='小红'; // a['[object Object]'] = '小红'
a[c]='小明'; // a['[object Object]'] = '小明'
console. log(a[b]); // '小明'
# 运算符优先级
[例题3]
运算符优先级比较
运算符优先级比较
a. x = a = {};
a = a. x = {};
因为成员访问 a. x 的优先级是很大的,所以无论怎么调换位置,都是先处理 a. x={}
var a = {n: 1};
var b = a;
a. x = a = {n: 2};
console. log(a. x);
console. log(b);
[解题步骤]
[循环引用问题]
循环引用 (堆内存只有一个,并不是内存溢出,是地址间的重复引用)
let x = {
name: 'xxx'
};
x. n = n;
console. log(x);