JavaScript作用域链

85 阅读1分钟

1、函数作用域

JS中只有函数级别的作用域,没有块级别的作用域;即,只有在进入或者退出函数的时候,作用域会发生变化。

1.1、概述

  • 外部对内部可见;
  • 内部对外部不可见;
  • 外部内部都有,就内部优先

1.2、例子

1.2.1 外部对内部可见

var scope = 'g';
function t(){
  console.log(scope); // g
  console.log(scope); // g
}
t();

1.2.2 内部对外部不可见

function t(){ 
  var scope="l"; 
} 
t(); 
console.log(scope); //scope is not defined

1.2.3 都可见,内部优先

var scope = 'g';
function t(){
	console.log(scope); // undefined
	var scope = '1';
	console.log(scope); // 1
}
t();

1.2.4 JS的作用域,都是函数级别的

// 小案例1
var scope = 'g';
if(true){
  var scope = '1';
  console.log(scope); //1
}
console.log(scope); //1,如果是java结果为g

// 小案例2
for(var i=0;i<10;i++){ } 
console.log(i); //10

2、执行环境(EC)

2.1、概述

执行环境execution context,定义了执行期间可以访问的变量和函数

2.2、分类

2.2.1、全局执行环境

  • Global Object (window)
  • 从见到js代码开始创建
  • 到网页关闭时销毁

2.2.2、函数执行环境

  • Activation Object
  • 从函数调用开始创建
  • 最后到函数调用结束时销毁

3、作用域链与生成作用域链

3.1、作用域链

  • 作用域[[scope]],每个函数都有
  • 作用域是私有属性,只能由JS引擎访问
  • 作用域链由AO和GO构成
  • 所谓执行环境,就是根据作用域链一次查找变量和函数
    • 找到即停
    • 全部找完无果,报错
  • 作用域链每个函数都有
  • JS的栈在作用域链中

3.2、生成作用域链

  • 函数【定义时期】拷贝父亲的作用域链;
  • 函数【调用时期】生成AO将AO压入作用域链栈顶;

3.3、生成作用域链相关示例

3.3.1、代码

var g = 'g';
function fa(){
  var a = 'a';
  function fa(){
    var b = 'b';
  }
  fb();
}
fa();

3.3.2、代码分析图解

image.png

3.4、生成作用域链相关思考

3.4.1、函数多次调用时,是产生相同的AO还是不同的AO?

function fa(){
  console.log(a); // undefined
  var a = 100;
  a++;
}
fa(); // 会有一个新的Scope Chain,同时产生自己的AO
fa(); // 会有一个新的Scope Chain,同时产生自己的AO
fa(); // 会有一个新的Scope Chain,同时产生自己的AO

3.4.2、函数递归调用时,时产生相同的AO还是不同的AO?【不同的AO】

var b = 15;
function fa(x){
	if(x > 2){
		var a = 100;
		a++;
		fa(x-1);
	}
	return 0;
}
fa(3);

3.4.3、除了函数还有什么可以影响作用域链

// 像内部优先
    var name = "weiran"
    person={
        name:"weiranyi",age:18,height:175,
        dog:{name:"yi",age:3}
    }
    with(person){
        console.log(name);
    }
     with(person.dog){
     console.log(name);
    }
    console.log(name);

3.5、作用域链应用

3.5.1 提高效率

  • 尽量少使用靠近上层的变量,多使用局部变量

3.5.2 重名,易出错

  • 尽量减少不同层次函数使用相同的变量名
  • 避免函数名与变量名一样

3.5.3 承上启下(函数退出后AO是否一定被释放:不一定)

function outer(){
	var scope = 'outer';
	function inner(){
		return scope;
	}
	return inner;
}

var fn = outer();
console.log(fn()); // outer