# 执行环境栈和执行上下文

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);