函数作用域与作用域链理解

先来个很有意思的栗子:

// 函数作用域
var scope=”scope”;
function fun(){
document.writeln(scope); //undefined
var scope=”local”;
document.writeln(scope); //local
}
fun();
document.writeln(scope); // scope

var wer = “global”;
function fv(){
document.writeln(wer); // global
wer = ‘function’;
document.writeln(wer); // function
}
fv();
document.writeln(wer); // global
为什么会出现这样?显而易见,var这个关键字申明影响到了代码执行的不同
那么var关键字申明是怎样的呢?

  • var num = 1; // 在当前域中申明一个变量(如果在函数中申明,称为局部变量,并影响该函数整个局部域的num量)
  • num = 1; // 这个事实上是对num属性赋值操作。它会在当前作用域中找到num并赋值,如果没有没有找到,即会在作用链的最顶层对象(全局对象中创造num并赋值)

其他申明标识符的方式

  • const申明的变量不可更改,而且必须初始化
  • let是在块级作用域申明(在严格模式下使用)。比如函数内部使用let申明的变量,对函数外部无影响

PS : 1. 通过var关键字申明的变量具有不可删除属性,而给属性赋值操作的可以被删除; 2. 在JS严格模式下,未申明的标识符赋值会被抛出异常,可以防止申明变量的错误

作用域和作用域链:

首先要明确js没有块级作用域!

作用域就是变量与函数的可访问范围,即控制着变量和函数的生命周期。有全局作用域和局部作用域两种
作用域链:

  • 首先它是一条变量对象的链,用于处理标识符的时候进行检索
  • 而函数的作用域链则是在执行的时候创建,存储函数内包含的变量和[[scope]],用于执行上下文。
    在一同创建的执行环境中,作用域链(比如 aContextScope = [aContext.AO,globalContext.VO]

PS: 1. JS执行上下文(EC): 比如执行函数代码块时,传参,先将存在的函数申明提升VO,再变量申明提升AO;2. 栈底总是全局上下文,栈顶是当前活动对象的上下文

这个可能很抽象……
那就举个栗子吧(以变量申明来说):

// 函数作用链
var name = “pengda”;
function a(){
document.writeln(name);
}
function b(){
var name = “penger”;
a();
}
b(); // pengda

利用EC和作用域链就很好解释了:

执行b的时候,因为JS没有块级作用域,所以a的上一级作用域就是全局作用域。根据EC和作用域链,结果输出为 pengda

思考: 当将function b() 代码改为 this.name = “penger” , 输出结果便为penger;