Js模仿块级作用域与ES6块级作用域

作者 LoaingMore 日期 2016-12-22
Js模仿块级作用域与ES6块级作用域

最近接触ES6多了起来,感觉自己之前js的基础还没有多牢固,趁着下班时间,着手温习下高程3,今天看到块级作用域这块,索性就把刚学一点的ES6做个比较,看异同,现学现卖了。

前传

ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

为什么需要块级作用域(ES5实现方式)?

第一种场景,用来计数的循环变量泄露为全局变量。

function outputNums(count){
for(var i=0;i<count;i++){
console.log(i);// 0 1 2 3 4 5 6
}
console.log(i); //7
}
outputNums(7)

该函数定义了for循环,变量i初始值为0,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。在Javascript中,变量i是定义在outputNums()的活动对象中,因此函数内部可以随处访问。

  • 解决方法:匿名函数可以用来模仿块级作用域
    (function(){
    //这里是块级作用域
    })()
    以上立即执行函数相当于
    var func = function(){
    //这里是块级作用域
    };
    func();

定义一个匿名函数,立即调用它,调用方式是在函数名字后面添加一对圆括号,即func();
如果省去变量直接加()呢?

function(){
//这里是块级作用域
}();//报错

因为JavaScript将function关键字当做一个函数声明的开始,而声明后面不能跟圆括号,然而表达式后面可以跟圆括号,将函数声明转化为函数表达式,只需如下:

(function(){
//这里是块级作用域
})();

改写后:

function outputNums(count){
(function(){
for(var i=0;i<count;i++){
console.log(i);// 0 1 2 3 4 5 6
}
})();
console.log(i); //报错
}
outputNums(7)

我们在for循环外面加了一个私有作用域,在匿名函数定义的任何变量,都会在执行结束时销毁,因此i只能在循环中使用,使用后即被销毁,在私有作用域中可以访问变量count,是因为匿名函数是一个闭包,可以访问包含作用域中的所有变量。
举个例子:

(function(){
var nowDate = new Date();
if(nowDate.getMonth == 0 && nowDate.getDate == 1){
alert("新年快乐!");
}
})()

上面这段代码,会在阳历一月一日,弹出新年祝福!在多人开发团队合作中,通过创建私有作用域,每个开发人员既可以使用自己的变量,也不用担心弄乱全局作用域。

第二种场景,内层变量可能会覆盖外层变量。

var nowDate = new Date();
function func(){
console.log(nowDate);
if(false){
var nowDate = "新年快乐";
}
}
func()//undefined

导致导致上面的结果原因是变量提升,导致内层nowDate覆盖外层nowDate;
错误还原如下:

var nowDate = new Date();
function func(){
var nowDate;
console.log(nowDate);
if(false){
nowDate = "新年快乐";
}
}
func()//undefined

ES6的块级作用域

let实际上为javascript增加了块级作用域

function func(){
let n = 5;
if(true){
let n = 10;
}
console.log(n);//5
}

上面两段代码都声明了n,运行后输出5,这表明外层代码不收内层代码的影响,如果用var 定义变量n,最后结果会输出10.

  • ES6允许块级作用域的任意嵌套。
    {{{{ let n = "新年快乐" }}}}

下面这段代码使用了四层的会计作用域,外层的作用域无法读取内层作用域。

{{{
{ let n = "新年快乐" }
console.log(n);//报错
}}}

内层作用域可以定义外层作用域的同名变量。

{{{
let n = "我是n"
{ let n = "我也可以是n" }
console.log(n);//我是n
}}}

展望下未来

块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了。

// IIFE 写法
(function () {
var nowDate = "新年快乐";
//...
})();
// 块级作用域写法
{
let nowDate ="新年快乐";
//...
}

结语:具体怎么用,还是得看支持的程度。
共勉!