JavaScript之作用域的认识

87 阅读5分钟

JavaScript之作用域的认识

// obj['name']:获取成员为name的属性值
// obj[name]:把name变量存储的值作为成员获取其属性
/* 
 * 对象的属性名:
    1、字符串
    2、Symbol
    3、数字/布尔/null/undefined等基本数据类型值(用这些值直接处理和基于他们的字符串格式处理结果是一样的)
    obj[true] === obj['true'] === obj.true  是一样的
*/
// 对于普通对象来说,其属性名不能是引用数据类型,设置为对对象的话也会被转为字符串;但是ES6中新增的Map数据结构中允许属性名的一个对象
 var obj = {};
obj[true] = 13;
obj[null] = 15;
obj[Symbol(111)] = 111;
console.log(obj.null, obj['Symbol(111)']) // 15 undefined  Symbol作为属性名是没有被转为字符串的 
 var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x); // unbdefined
console.log(b);  // {n:1, x: {n: 2} } 
/* 
 解析:
   第一点:对于连等 a = b = 12;这种是从右往左进行赋值,即拆分开来就是 b = 12; a = b;
   第二点:运算符运算优先级问题,成员访问.的优先级高于赋值=的优先级,即 a.x = b = 3;拆分开来就是 a.x = 3; b = 3
*/
/* 
 * GO(全局对象):浏览器最开始加载代码的时候,不仅提供了一个栈内存供代码执行,而且还默认开辟了一个堆内存,用来存储一些内置的属性和方法
   !注意:全局对象GO跟全局变量对象VO(G)不是同一个东西
   二者之间的关系:VO(G)中有一个叫做window的变量,变量值是GO
*/

/* 
 * 在 “全局上下文中”,基于 “var/function” 声明的全局变量,也会给GO(window)中新增一个对应的私有属性,并且和全局变量有“映射机制”,即其中一个修改,另一个也会跟着被修改
*/
 var a = 10; // 1、声明一个全局变量a=10;1、给window新增一个私有属性 window.a = 10;
console.log(a); // 首先看看是不是全局变量,如果不是就去看看是不是window的属性,如果也不是那就报错: a is not defined
console.log(window.a); // 10 
// 基于 “let/const” 声明的全局变量跟window(GO)没有关系
 let a = 10;  // 全局变量a
console.log(a); // 10
console.log(window.a); // undefined 
// 在全局上下文中
 a = 10; // a不是全局变量,而是 window的一个属性,相当于省略了window(前提:确定a没有被声明过)
console.log(a); // 10 是因为全局变量跟GO有映射机制 

函数的存储和执行

/* 
 * 创建函数:
    1、开辟新的堆内存(16进制地址)
    2、把函数体中的代码当做字符串存储到开辟的堆内存中(当然也有键值对,函数也是对象)
    3、把地址存到栈中,跟变量(函数名)进行关联
    // => 创建函数的时候,就声明了函数的“作用域”,值是当前函数创建时所处的上下文
    // => 如果创建了函数但是不执行(调用),那没有什么用,只是存储了一堆字符串

  * 函数执行:
      目的:是把之前创建函数的时候在函数体中存储的代码字符串进行执行
      1、形成一个全新的私有上下文:
        + 有存储当前私有上下文中声明的私有变量的空间AO(AO是VO的一种,只是全局的叫VO,私有的叫AO)
        + 把上下文进栈执行
      2、代码执行之前要做好多事
        + 初始化作用域链:作用域链(scope-chain)有两端,左端:当前上下文;右端:函数作用域(函数创建时所处的上下文)
        + 初始化this的指向
        + 初始化argments(参数对象)
        + 形参赋值:形参是私有变量
        + 变量提升
      3、代码执行
      4、一般情况下,浏览器在函数执行完之后,为了优化栈内存,会把形成的私有上下文进行出栈释放掉(GC浏览器垃圾回收机制)

      作用域链:当前私有上下文代码执行,遇到一个变量,首先看是否为私有变量,如果是私有的,则接下来用到的都是私有的,和其他上下文中的变量没有任何直接关系;如果不是自己私有的变量,则按照作用域链向上级上下文中查找,如果上级也没有,继续往上级找......一直找到全局上下文;如果全局也没有,有两种情况:1、如果是设置值,那么相当于是给window设置属性;2、如果是获取值,那么就会报错
*/
// 题目:
var x = [12,32];
function fn(y) {
  y[0] = 100;
  y = [100];
  y[1] = 200;
  console.log(y); // [100, 200]
}
fn(x);
console.log(x); // [100, 32]

/* 
 fn(x)执行:
   全新的执行上下文:EC(fn)
   这里的私有变量有两种:形参和当前上下文中声明的变量
   初始化作用域链:[[scope-chain]]: <EC(fn), EC(G)>
   初始化this指向:this: window
   初始化arguments:arguments: {0: AF0, length: 1}
   形参赋值:y = AF0
   变量提升:无
   代码执行:
   y[0] = 100: AF0[0] = 100
   y = [100];   y = AF1
   y[1] = 200;  AF1[1] = 200
   console.log(y);  输出 AF1,即[100, 200]
*/
// 函数执行,会形成一个私有的上下文,保护里面的私有变量不受外界干扰。这种保护机制称之为“闭包”