D2-堆栈内存及函数底层运行机制

190 阅读6分钟
1.对象属性名相关知识:

练习题:写出下列代码的输出结果

//example 1
var a={}, b='0', c=0;  
a[b]='你好';
a[c]='世界';  
console.log(a[b]);
​
---------------------

//example 2
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[c]='世界';  
console.log(a[b]);

obj['name'] 获取成员为name的属性值 obj[name] 把name变量存储的值作为成员获取其属性值 对象的属性名可以是:

  • 字符串
  • Symbol;
  • 数字/布尔/null/undefined等基本数据类型值(用这些值直接处理和基于他们的字符串格式处理结果是一样的)

也有人认为属性名除了Symbol只能是字符串格式的 对于普通对象来讲,属性名不能是引用数据类型值(比如对象),设置为对象,也会转换为字符串;ES6中新增Map数据结构,这个结构中可以允许属性名是一个对象; 在这里插入图片描述

var obj = {
    name: 'xxx',
    0: 100,
    true: 11,
    null:10,
    'null':12,
    undefined:99
};
obj[Symbol('AA')] = 100;

var o1 = {
    name: 'xxx'
};
obj[o1] = 100; // obj["[object Object]"]=100
console.log(obj); 
/*//输出结果
{
  '0': 100,
  name: 'xxx',
  true: 11,
  null: 12,
  undefined: 99,
  '[object Object]': 100,
  [Symbol(AA)]: 100
} 

 // Symbol类型不能直接写对象属性
 var obj={
    a:1,
    Symbol('a'):10
}
//要写出下面这样
obj[Symbol('a')]=10;

现在分析例题:

//example 1
var a={}, b='0', c=0;  
a[b]='你好';   //=>a['0']='你好'
a[c]='世界';   //=>a[0]='世界';
console.log(a[b]); //=>'世界'

//example 2
var a={}, b=Symbol('1'), c=Symbol('1');  //b!==c唯一值
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]); //=>'世界'
2.JS底层运行机制之堆(Heap)栈(Stack)
  • ECStack(Execution [ˌeksɪˈkjuːʃn] Context Stack)和 EC(Execution Context )
  • GO(Global Object)
  • VO(Varibale Object)

练习题:写出下列代码的输出结果

var a = {
    n: 1
};
var b = a;
a.x = a = {
    n: 2
};
console.log(a.x);
console.log(b); 

此题的图解: 在这里插入图片描述 图解分析:

  1. 浏览器为了代码运行提供环境即栈内存【执行环境栈ECStack】,栈内存就是在计算机中分配出来的一块内存;
  2. 全局代码执行前形成全局执行上下文EC(G),全局代码都在EC(G)中执行,而EC(G)也要进入浏览器提供的这个执行环境栈ECStack中。在这个全局执行上下文中创建的变量总会存储在当前上下文中指定的**全局变量对象[VO (G)]**中。浏览器默认创建的一个全局变量window(在nodeJS中默认创建的全局变量是global)
  3. 浏览器最开始加载代码的时候,不仅提供了一个栈内存供代码执行;而且还默认开辟了一个堆内存,存储一些内置的属性和方法,即全局对象Global Object(GO)
  4. 全局对象GO!==全局变量对象VO(G),两者的关系只是:VO(G)中的一个变量window是属性名,属性值是GO,两者相互关联。
  5. “在全局上下文中”,基于“var/function”声明的全局变量,也会给GO(window)中新增一个对应的私有属性,并且和全局的变量有“映射机制”:一个修改,另外一个也会跟着修改
// 1.声明一个全局变量a=10   2.给window新增一个私有属性 window.a=10
var a = 10; 

console.log(a); 
//首先看a是否为全局变量,如果是按照全局变量处理,如果不是全局变量,再看是否为window的一个属性,如果也不是window的属性则报错:a is not defined

console.log(window.a); 
//直接访问对象的成员

window.a = 20; //“映射机制” 全局变量a=20
console.log(a);

//-----------------------------------------------
// 基于“let/const”声明的全局变量和window没有关系
let a = 10; //全局变量a
console.log(a); //10
console.log(window.a); //undefined 

//----------------------------------------------
// 在全局上下文中
a = 10; //不是全局变量,window.a=10,相当于省略了window(前提:确定之前没有声明过)

==带声明的关键词和不带是不一样的机制,所以真实项目中请大家不要省略== 6. 连等赋值 在MDN网站查看“运算符优先级”,赋值等号的优先级是3,从右到左,所以【var a=b=10】=>【先b=10;再var a=10;】。【a.x=b={}】,因为a.x的优先级高(点成员访问的优先级是19),所以【先a.x={},再b={}】

3.函数底层运行机制

全面剖析函数的底层处理机制

  • EC(Execution Context )
  • AO(Active Object)
  • SCOPE
  • SCOPE-CHAIN

练习题:写出下列代码的输出结果

var x = [12, 23];
function fn(y) {
    y[0] = 100;
    y = [100];
    y[1] = 200;
    console.log(y);
}
fn(x);
console.log(x);

此题的图解: 在这里插入图片描述 由此题所引申的知识点: 创建函数: 1.开辟堆内存(16进制地址) 2.把函数体内部的代码,当作“字符串”存储到堆中[当然也有键值对,函数也是对象] 3.把地址存到栈中,供变量(函数名)调用 =>创建函数的时候,就声明了函数的“作用域”,值是当前函数创建时所处的上下文 创建函数但不执行,毛用都没有,存储的一-对破字符串

函数执行 目的:是把之前创建函数时候,在函数体中存储的代码字符串去执行的

  1. 形成一个全新的私有上下文
    • 有存储当前私有上下文中声明的私有变量的空间AO,AO(ActiveObject)他是VO的一种
    • 把这个私有上下文进栈执行
  2. 代码执行之前要做好多事情
    • 初始化作用域链
    • 初始化this指向
    • 初始化arguments
    • 形参赋值
    • 变量提升
  3. 代码执行
  4. 一般情况下,函数执行完成后,为了优化栈内存,会把形成的私有上下文出栈释放掉“GC浏览器垃圾回收机制”
  • 作用域链查找机制:当前私有上下文下代码执行,遇到一个变量,首先看是否为私有变量,如果是私有的,则接下来操作,都是操作自己私有的,和其他上下文没有任何直接的关系;如果不是自己私有变量,则按照作用链,向上级_上下文中查找...如果上级也没有,一直找到全局上下文;如果全局也没有设置变量值:相当于给window设置属性值。如果是获取这个值:报错。
  • 闭包:函数执行,会形成一个私有的上下文,保护里面的私有变量不受外界的干扰,我们把函数执行的这种保护机制,称之为“闭包”
  • 作用域链scope--chain:<EC(fn),EC(G)> 左端EC(fn):当前上下文 右端EC(G):函数的作用域(函数创建时候所处的上下文)
  • 进栈执行(栈结构:进栈执行的内容都在栈的顶端)
  • 函数中的私有变量:形参是私有变量;当前上下文中声明过的变量是私有变量。