再探javascript作用域

对于javascript作用域的理解,之前整理过两篇文章:《javascript作用域和作用域链》《javascript的词法作用域》。最近读了周爱民老师的《javascript语言精髓》,对javascript作用域有了新的认识,特地整理下思路。

javascript的作用域广义来分:语法作用域变量作用域

javascript语法作用域

javascript语法作用域是讨论代码的组织结构上抽象,可以理解为讨论的是“圈地”问题,也有书籍中对纯粹的语法作用域称为“静态作用域”。

语法作用域分四级,下标依次增高

级别 作用域
级别一 表达式
级别二 语句/批语句
级别三 函数
级别四 全局(宿主)
  1. 同级别可以平行也可以相互嵌套(包含)
  2. 高级别可以嵌套(包含)低级别
  3. 低级别不可以嵌套(包含)高级别作用域;假如低级别嵌套高u别,可以理解成相互平行,例如下面的代码是完全等效的
if(true){
    //
    function fn(){
        //
    }
}

if(true){
    //
}
function fn(){
    //
}

批语句作用域

批语句作用域的“{}”是用来声明被影响的代码,如果单纯理解为复合语句的“{}”则会报错,例如下面的代码:

try
    var a = 2;
catch(e);

批语句作用域包括:try{}catch(e){}finally{}switch

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

spidermonkey 1.7 添加了let关键字,所以:

for( let i = 0; i < 10; i++){ } console.log(i);//报错,undefined

javascript变量作用域

变量的可见性 和 生存周期;即大家常说(或者常被理解)的作用域。

变量作用域完成对信息的隐蔽,也就是处理“割据”的问题,也有书籍中对执行期的变量作用域为“动态作用域”。

变量作用域没有语句级别,分为:表达式函数全局三个级别。

表达式

表达式级别作用域,变量创建出来被立即使用。

表达式级别作用域,都是匿名的。

var date = 'date:'+new Date;
var foo = (100 + function(){
    return 100
});

函数

局部变量,对内可见;对外不可见

全局

在全局范围内任意声明变量。

在函数内部不使用var 声明而直接赋值的变量,该变量会被隐式的在全局中声明

全局变量可以理解为Global对象中的成员,也可以被解释为宿主对象,在浏览器中就是window对象

显性声明和隐性声明 虽然都是全局变量,但是而这处理规则却不同

var a = 100;
delete a; //因为dontDelete为false,所以返回false
a = 100;
delete a; //因为dontDelete 为true,所以返回true

总结下javascript的作用域

  1. javascript没有块级作用域
  2. 语法作用域不等于变量作用域
  3. 变量的可见性受限于它所在的语法结构的(语法)作用域
  • 补充:如果语言没有实现相应的变量作用域,那么该变量的可见性会溢出到同级的其他结构中,例如 if、for块级语句
  • 特例:spidermonkey javascript 1.7 let 关键字会实现语句级别的变量作用域

变量声明次序问题

javascript不同于C和java语言,javascript是解释执行的语言。语法解析会在执行之前,语法解析会建立一个类似“变量表”的对象,并且赋值为undefined

if ( !a ){
    alert(a); //undefined
    var a = 1;
}
alert(a); //1

变量作用域和变量的生存周期

变量作用域讨论的是解决变量在哪个范围内存储;生存周期讨论的是代码执行中什么时候创建和释放一个变量。

创建

  1. js引擎做语法分析,发现显性的声明
  2. js引擎代码执行时,发现隐性的未被创建的变量时

释放

  1. js引擎执行到函数结束/退出时,将清除的函数的未被引用的变量
  2. js引擎执行到全局代码结束,或者引擎卸载和重新载入时,将清除全局的变量和数据引用

所以生存周期包括:函数内局部执行期间,函数外全局执行期间。变量的生存周期创建和释放过程就是内存的占用和释放过程