做用域和做用域链查找机制|内附思惟导图

咱们今天先从概念入手,而后在经过一道例题,完完整整的了解代码执行的过程,在这个过程当中会用到做用域和做用域链查找机制;咱们在根据概念的带入详细梳理;javascript

思惟导图

1、做用域的概念

在某一个上下文中建立函数,除了开辟内存和赋值以外,还会给当前函数设置一个做用域属性[[scope]]java

  • 当前函数[[scope]] = 当前函数建立时候所在的上下文 ;

简单来讲:当前函数的做用域取决于当前函数建立时候的上下文,它在哪一个上下文建立的,那它的做用域就是谁函数

2、全局变量 VS 私有变量

  • 全局变量:在全局上下文EC(G)中的全局变量对象VO(G)中,存储的变量
  • 私有变量:在函数执行造成的私有上下文EC(XXX)中的变量对象AO(XXX)中,存储的变量;

都有哪些是私有变量呢:ui

  • 当前函数执行造成的上下文中:声明过的变量或者函数,都会存储到AO(XXX)中,
  • 函数定义的形参变量,也会存储到当前上下文的AO(XXX)中;

3、做用域链查找机制

一、做用域链scopeChain的概念

做用域链scopeChain里面包含了:spa

  • 当前造成的上下文,
  • 以及当前函数所对应的scope做用域;

scopeChain:<当前EC,函数[[scope]]>;3d

以此来创建链式关系,以后咱们在查找变量的时候,就按照这个链式关系找(先找本身上下文的,本身没有,按照做用域链向上级做用域找)code

二、做用域链的造成

做用域链是在函数执行的时候造成的;执行函数的具体步骤为:cdn

  • 建立私有上下文EC(有存放私有变量的变量对象AO)
  • 进栈执行(时会把全局上下文压缩到底部)
  • 初始化做用域链 scopeChain:<当前EC,函数[[scope]]>
  • 初始化THIS指向
  • 形参赋值(包含初始化ARGUMENTS
  • 变量提高
  • ......这里咱们省略了一些
  • 代码执行
  • 执行完可能会出栈(也可能不出栈)

当函数执行的时候除了按照咱们上面的,还有不少不少操做;对象

三、做用域链的查找机制

  • 在当前上下文中,代码执行的过程当中遇到一个变量时:

首先看它是不是私有的blog

  • 1.若是是私有的,接下来的全部操做,都是操做本身的,和别人没有关系;
  • 2.若是不是私有的,则按照scopeChain做用域链进行查找,在哪一个上下文中找到,当前变量就是谁的
  • ......一直找到全局上下文为止
  • 若是找到EC(G)都找不到:
    • 是获取变量值就会报错,
    • 是设置值,至关于给GO加属性

4、以题为例:详细解析

一、一个小知识点:var a = b = 12 ;var a = 12, b = 12 ;的区别

在此以前咱们先了解一个零散的小知识点:

  • var a = b = 12 ;var a = 12, b = 12 ;的区别
var a = b = 12;
//=> 至关于:var a = 12; b = 12; 只有第一个带VAR,第二个不带VAR

var a=12, b=12;
//=> 至关于var a = 12; var b = 12; 连续建立多个变量,可使用逗号分隔;
复制代码

二、例题按步骤详解

好了,接下来开始咱们的正式内容;

console.log(a, b);
var a = 12,
	b = 12;
function fn() {
	console.log(a, b);
	var a = b = 13;
	console.log(a, b);
}
fn();
console.log(a, b);
复制代码

此题的操做过程:

  • 第一步:造成执行环境栈ECStack
  • 第二步:造成一个全局上下文EC(G)
  • 第三步:造成全局变量对象VO(G)
  • 第四步:进栈执行
  • ......:这里咱们省略一些咱们暂时用不到的内容
  • 第五步:变量提高;
    • var a ;
    • var b ;
    • function fn(){...} ; (这里声明+定义fn )
      • 开辟堆内存,生成一个空间地址(这里咱们假设堆的地址为AAAFFF000),把函数看成字符串存进堆中;
      • 与此同时,在建立函数的时候还会给函数加一个scope做用域属性
        • 做用域属性是指:当前函数是在哪一个上下文中建立的,那它的做用域就是哪一个上下文
        • 因此本题的fn[[scope]] = EC(G) ;
      • 把堆的地址AAAFFF000,看成值与 fn 变量关联在一块儿;
  • 第六步:代码执行;
    • 1.console.log(a, b); => undefined / undefined;
    • 2.var a = 12 ;
      • 首先建立值12;
      • (此时var a 操做已经在变量提高阶段完成,因此直接跳过);
      • 把变量a12关联;
    • 3.var b = 12 ; => 同上面var a = 12同样;
    • 4.function fn(){...}; => (变量提高阶段已经完成,直接跳过);
    • 5.fn() ; => 让fn函数执行;
      • 造成一个新的私有的执行上下文(咱们把它命名为EC(fn1));
      • 造成一个私有变量对象AO(咱们把它命名为AO(fn1));
      • EC(G)被压缩到ECStack底部,同时EC(fn1)进栈执行 ;
      • 此时是函数执行过程,也是咱们真正要讲解内容的开始,咱们按步骤命名 ;函数体中在代码执行以前,要作不少不少的事情,咱们这里省略不须要的内容

      • 第一步:初始化做用域链:
        • scopeChain:<当前EC,函数[[scope]]> 这里的fn[[scope]]咱们在函数建立时,已经知道是EC(G),因此最终的做用域链为=> scopeChain:<EC(fn1),EC(G)>
      • ......
      • 第二步:变量提高;
        • var a ;
      • 第三步:代码执行;
        • console.log(a, b); => 此时就按照做用域链查找,
          • 此时a已经存在,只不过没有赋值,因此a是本身的私有属性,直接操做本身的便可;=> undefined
          • b不是本身的私有属性,经过做用域链查找,找到EC(G)中的b,此后操做的都是EC(G)中的b; => 12
        • var a = 13 ; => 建立值13var a跳过,a13关联;
        • b = 13 ; => 经过做用域链找到全局的b;给b从新赋值13;
        • console.log(a, b); => 13 / 13;
        • 此时函数体中代码以所有执行完。
      • 第四步:出栈销毁,同时全局上下文恢复到原来位置,继续执行。
    • 6.console.log(a, b); => 此时打印全局下的a,b;
      • a => 12 ;
      • b => 13 ;
  • 执行完成

三、例题图解析

5、补充几道练习题

1.写出下面代码输出的结果(本题咱们画图解析)

console.log(a, b, c);//=> undefined undefined undefined
var a = 12,
    b = 13,
    c = 14;
function fn(a) {
    console.log(a, b, c);//=> 10 , 13 , 14
    a = 100;
    c = 200;
    console.log(a, b, c);//=> 100 , 13 ,200
}
b = fn(10);
console.log(a, b, c);//=> 100 undefined 200
复制代码

2.写出下面代码输出的结果(本题咱们画图解析)

var ary = [12, 23];
function fn(ary) {
    console.log(ary);//=> [12,23]
    ary[0] = 100;
    ary = [100];
    ary[0] = 0;
    console.log(ary);//=> [0]
}
fn(ary);
console.log(ary);//=> [100,23]
复制代码

3.写出下面代码输出的结果(本题咱们画图解析)

var a=1;
var obj ={
   "name":"tom"
}
function fn(){
   var a2 = a;
   obj2 = obj;
   a2 =a;
   obj2.name =”jack”;
}
fn();
console.log(a);//=> 1
console.log(obj);//=> {"name":"jack"}
复制代码

4.写出下面代码输出的结果(本题咱们画图解析)

var i = 20;
function fn() {
    i -= 2;
    var i = 10;
    return function (n) {
        console.log((++i) - n);
    }
}
var f = fn();
f(1);//=>10
f(2);//=>10
fn()(3);//=>8
fn()(4);//=>7
f(5);//=>8
console.log(i);//=>20
复制代码

5.写出下面代码输出的结果(本题咱们画图解析)

//========有形参=======
let x = 5;
function fn(x) {
    return function(y) {
        console.log(y + (++x));
    }
}
let f = fn(6);
f(7);//=>14
fn(8)(9);//=>18
f(10);//=>18
console.log(x);
//========无形参==========
let x = 5;
function fn() {
    return function(y) {
        console.log(y + (++x));
    }
}
let f = fn(6);
f(7);//=>13
fn(8)(9);//=>16
f(10);//=>18
console.log(x);
复制代码
  • 有形参x的状况
  • 无形参的状况

相关文章
相关标签/搜索