javascript中的作用域
js的作用域,按工作模型,可以分为:词法作用域和动态作用域。按类型,可以分为:函数作用域和块作用域。
一、什么是词法作用域
词法作用域是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的。在编译阶段进行静态确定的形式。
/* 全局作用域有:foo foo的作用域有: a b bar bar的作用域有: c */ function foo(a){ let b = a * 2; function bar(c){ console.log(a,b,c); } bar( b * 3 ); } foo(2); //2,4,12
二、什么是动态作用域
在运行时才动态确定的形式。关注函数从何处调用。不过JavaScript并不具有动态作用域。都是词法作用域。但javascript中的this机制却很像动态作用域。
function foo(){ console.log(a); } let a = 2; function bar(){ let a = 3; foo(); } bar(); //2 (这里是词法作用域) 调用foo() 是找不到变量a, 向上一级作用域(这里是全局作用域)继续查找,然后找到了a = 2;
三、函数作用域
特点:词法作用域意味着作用域是由书写时函数声明的位置来决定的。
function foo(a){ let b = 2; function bar(){ console.log("foo bar"); //foo bar //同样在bar ( . . ) 内部也可以被访问 console.log(a); //1 } let c = 3; //但是,这些标识符(a、b、c、f oo 和bar)在f oo( . . ) 的内部都是可以被访问的, console.log(a,b,c,bar); bar(); } foo(1);//1 2 3 f bar(){console.log('foo bar'); console.log(a);} // 标识符 a、b、c 和 bar 都附属于 f oo( . . ) 的作用域气泡,因此无法从 f oo( . . ) 的外部对它们进行访问 //console.log(a); //a is not defined //console.log(b); //b is not defined //console.log(c); //c is not defined //console.dir(bar); //bar is not defined
优点:(1)能隐藏内部实现,最小限度地暴露必要内容,符合软件设计的原则。(2)规避冲突(常用的做法有:声明一个独特的对象作为给库的命名空间;使用模块管理)
//对外暴露的不必要的变量:b 和 bar let b; function foo(a){ b = bar(a+2); return b+a; } function bar(a){ return a*2; } console.dir(foo(11)); //37 //改写成: function foo(a){ function bar(a){ return a*2; } let b = bar(a+2); return b+a; } console.dir(foo(11)); //37
缺点:(1)本身函数名暴露了,可以使用立即执行函数表达式。
//对外暴露了函数名 function foo(a){ console.log(a); } foo(2); //改成立即执行函数表达式 (function foo(a){ console.log(a); })(2); foo(2); //foo is not defined
四、块作用域
优点:防止变量外泄;垃圾回收
形成过程:ES3开始,只能通过with 和 try/catch 来生成块作用域;ES6提出了 let 和 const 关键字,来得到块作用域
// 有效 ES6环境下的代码转换成能在ES6之前环境运行的代码转换工具的原理: try{ throw undefined; }catch(b){ b = 2; console.log(b); //2 } console.log(b); //b is not defined //无效,外界还是能访问 if(true){ var a = 1; console.log(a); //1 } console.log(a); //1 //改为es6的写法 有效 if(true){ let a = 1; console.log(a); //1 } console.log(a); //a is not defined
注:var 与 let 和 const 的区别除了块作用域外,还有声明的提升
// var声明提升 console.log(a); //undefined var a = 1; console.log(a); //1 //相当于 var a; console.log(a); //undefined a = 1; console.log(a); //1 console.log(b); //报错:b is not defined let b = 2; console.log(b);