JS中作用域和预编译以及作用域链的剖析


一、作用域

1. 全局作用域
写脚本块中的代码
全局作用于中声明的变量,会被提前到代码块的顶部进行定义,成为全局对象的属性
2. 函数作用域
函数内部定义的变量,会被提升到函数代码块中的顶部,并且不会成为全局对象的属性

1.全局作用域

代码如下(示例):

console.log(a);//undefined  声明一个空间 并赋默认值 undefined
var a = 10;
console.log(a);//10
console.log(window.a);// 作用域上找不到会报错;原型链上找不到显示undefined

2.函数作用域(局部作用域)

代码如下(示例):

function test(){
    
    
    var a = 10;
    console.log(a);//10
}
test();
console.log(a);//报错(a is not defined)
function demo(){
    
    
    var a = 10;
    b = 20;// 直接赋值未声明会变成全局变量,会造成全局污染
    console.log(a,b);// 10 20
}
demo();
console.log(b);  // 20
console.log(a);  // 报错
function fn(){
    
    
    // var x = y = 20;	//这一行代码可以拆分为下面两行代码
    var x = y;// 代码执行到这一行会报错,不会有任何输出(y is not defined)
    y = 20;
    console.log(x);
    console.log(y);
}
fn();
console.log(y);
console.log(x);
function fn(){
    
    
   var x = y; 
   var y = 20;
   console.log(x);	//undefined
   console.log(y);	//20
}
fn();
console.log(y);		//报错
console.log(x);

函数内部定义的变量,会被提升到函数代码块中的顶部
下边是上面函数执行的解析:

function fn(){
   var x;	//提前声明
   var y;	//提前声明
   x = y; 	//因为此时y只定义了未赋值,所以把默认值undefined赋给x
   y = 20;	//把20赋给y
   console.log(x);	//undefined
   console.log(y);	//20
}
fn();

二、预编译

简单来说预编译就是变量和函数的提前声明
js代码执行三部曲:
1.语法检测
2.预编译
3.代码执行

1.全局预编译

  1. 创建一个GO对象 Global Object
  2. 将var关键字声明的变量当作GO对象的属性,赋值为undefined,有重名的直接覆盖
  3. 将function关键字声明函数 当作GO对象的属性,值为函数体,重名直接覆盖

2.函数预编译

函数在执行的前一刻开始会经历以下过程:

  1. 创建一个AO对象 Activation Object 执行期上下文对象
  2. 函数的形参,成为AO对象的属性,值为实参的值,若未传值,值为undefined
  3. 将var关键字声明的变量,成为AO对象的属性,值为undefined,遇到已经存在的,不做任何变化
  4. 将function声明的函数 成为AO对象的属性 值为函数体,重名直接覆盖

3.代码举例

例1:

console.log(a);//undefined
var a = 10;
console.log(a);// 10
console.log(test); // function test(){console.log('Roddy');}
test();	//Roddy
function test(){
    
    
    console.log('Roddy');
}
test();	//Roddy

解析

GO{
      a:undefined
      test:function test(){
              console.log("Roddy");
      }
      // 代码执行到a=10
      f:10
}

例2:

function ld(){
    
    
    console.log(1); // 3
}
f();
function ld(){
    
    
    console.log(2); // 3
}
f();
function ld(){
    
    
    console.log(3);// 3
}
ld();
var ld = 100;
console.log(ld); // 100

解析

GO{
      ld:undefined
      // 声明结束后
      ld:function ld(){
                console.log(3);// 3
      }
      // 代码执行到ld=100,ld函数体再次被覆盖
      f:100
}

三、作用域链

  • 万物皆对象,函数也是一个对象
  • function test(){}
  • test.name test.prototype
  • 函数中隐式的属性 test.[[scope]]
  • [[scope]] : 指的就是我们所说的作用域,就是存储运行期的执期上下文集合。
  • 作用域链 : [[scope]] 中存储的执行器上下文对象的集合,这个集合呈现链式链接,我们把这种链式链接叫做作用域链。
  • 执行期上下文对象:当函数执行时,创建一个执行期上下文,定义了函数执行时的环境,函数每次执行时,执行期上下文都是独一无二的,所以每调用一次函数都会创建一个独立的执行期上下文,当函数执行完毕时,它所产生的执行期上下文会被销毁。

例1:

function outLuodi(){
    
    
    function inLuodi(){
    
    
        var b = 666;
    }
    var a = 999;
    inFun();
}
var glob = 520;
outFun();

解析:

GO:{
  glob:undefined,
  outLuodi:function,
  glob:520
} 
outLuodi函数在定义时
outLuodi.[[scope]]    0:GO存入到[[scope]]中   
outLuodi函数在调用时创建一个AO对象  
AO{
    a:999
    inLuodi:function
}

outLuodi.[[scope]]    0:AO
               		  1:GO存入到[[scope]]中

inLuodi函数定义时
inLuodi.[[scope]]	0:outLuodi AO
				    1:GO存入到[[scope]]中
inLuodi函数调用时创建一个AO对象  
AO{
 b:666
}

inLuodi.[[scope]]	0:inLuodi AO
					1:outLuodi AO
					2:GO存入到[[scope]]中

outLuodi被定义时
在这里插入图片描述

outLuodi被执行时
在这里插入图片描述

inLuodi被定义时
在这里插入图片描述

inLuodi被执行时
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/RoddyLD/article/details/114440347