对于javascript作用域的理解,之前整理过两篇文章:《javascript作用域和作用域链》《javascript的词法作用域》。最近读了周爱民老师的《javascript语言精髓》,对javascript作用域有了新的认识,特地整理下思路。
javascript的作用域广义来分:语法作用域和变量作用域。
javascript语法作用域
javascript语法作用域是讨论代码的组织结构上抽象,可以理解为讨论的是“圈地”问题,也有书籍中对纯粹的语法作用域称为“静态作用域”。
语法作用域分四级,下标依次增高
级别 | 作用域 |
---|---|
级别一 | 表达式 |
级别二 | 语句/批语句 |
级别三 | 函数 |
级别四 | 全局(宿主) |
- 同级别可以平行也可以相互嵌套(包含)
- 高级别可以嵌套(包含)低级别
- 低级别不可以嵌套(包含)高级别作用域;假如低级别嵌套高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的作用域
- javascript没有块级作用域
- 语法作用域不等于变量作用域
- 变量的可见性受限于它所在的语法结构的(语法)作用域
- 补充:如果语言没有实现相应的变量作用域,那么该变量的可见性会溢出到同级的其他结构中,例如 if、for块级语句
- _特例_:spidermonkey javascript 1.7 let 关键字会实现语句级别的变量作用域
变量声明次序问题
javascript不同于C和java语言,javascript是解释执行的语言。语法解析会在执行之前,语法解析会建立一个类似“变量表”的对象,并且赋值为undefined
。
if ( !a ){
alert(a); //undefined
var a = 1;
}
alert(a); //1
变量作用域和变量的生存周期
变量作用域讨论的是解决变量在哪个范围内存储;生存周期讨论的是代码执行中什么时候创建和释放一个变量。
创建:
- js引擎做语法分析,发现显性的声明
- js引擎代码执行时,发现隐性的未被创建的变量时
释放:
- js引擎执行到函数结束/退出时,将清除的函数的未被引用的变量
- js引擎执行到全局代码结束,或者引擎卸载和重新载入时,将清除全局的变量和数据引用
所以生存周期包括:函数内局部执行期间,函数外全局执行期间。变量的生存周期创建和释放过程就是内存的占用和释放过程