资讯专栏INFORMATION COLUMN

es6块级作用域

cnTomato / 623人阅读

摘要:块级作用域存在于两个地方函数内部。如下例应该返回创建数组的构造函数即这是数组返回这是数组从上例,我们可以知道即使全局作用域中已经定义了变量或者已经存在了属性,但我们之后定义的变量则会覆盖之前已经定义好的或者已经存在的变量属性。

一.var 声明与变量提升机制

JavaScript中使用var定义一个变量,无论是定义在全局作用域函数函数的局部作用域中,都会被提升到其作用域的顶部,这也是JavaScript定义变量的一个令人困惑的地方。由于es5没有像其它类C语言一样的块级作用域,因此es6增加了let定义变量,用来创建块级作用域。

我们来看一个var定义变量的示例:

function   setName(){
    if(condition){
       var name = "loho";
       console.log(name);
    }else{
       console.log(name);
    }
}

var student = "eveningwater";
setName();

以上代码可以理解成如下:

var student;
function setName(){
   var name;
   if(condition){
       name = "loho";
       console.log(name);//loho
   }else{
      console.log(name);//undefined
   }
}
student = "eveningwater";
setName();
二.块级声明

块级声明意在指定一个块级作用域,使得块级作用域中所定义的变量无法再全局被访问到,块级作用域也被称为词法作用域。

块级作用域存在于两个地方:

函数内部。

指定代码块中。(即"{"和"}"之间的区域)

1.let 声明

let声明同var声明用法一致,唯一的区别在于,let声明将变量限制在一个块内,这样就形成了一个块级作用域,因此也就不会存在变量的提升了。

例如前面的示例,我们可以写成如下:

let stundent = "eveningwater";
function setName(){
    if(condition){
        let name = "loho";
       console.log(name);//loho
    }else{
       //如果条件为false执行到这里
       console.log(name);//不返回值
    }
}
setName();

2.禁止重声明

在使用let定义变量之前如果已经声明了相同的变量,就会报错。因此不能重复声明变量。如以下示例:

var name = "eveningwater";
//报错,重复声明
let name = "loho";

当然这两个变量必须是在同一个作用域中,如果是不同作用域中,则不会报错。但有可能会遮蔽第一次声明的变量。如以下示例:

var name = "eveningwater";
if(condition){
   //不会报错
   let name = "loho";
}
3.const声明

使用const标识符所声明的变量必须要初始化,因此这个声明的就是一个常量。如下例:

const name="eveningwater";//正确
const name;//错误,未初始化

const声明同let声明一样,也是创建了一个块级作用域,在这个块级作用域之外是无法访问到所声明的变量的。换句话说,就是const所声明的变量不会有变量提升机制。如下例:

if(condition){
        const name = "eveningwater";
        console.log(name);//"eveningwater"
}
//错误
console.log(name);

同样的const也不能重复声明,如下例:

var name = "eveningwater";
//错误,不能重复声明
const name = "loho";

但也可以在不同作用域中重复声明,如下例:

var name = "eveningwater";
if(condition){
    const name = "loho";
   console.log(name);//loho,屏蔽全局定义的变量
}

尽管const声明与let声明有太多相似的地方,但const声明也有一处与let声明不同,那就是const声明的变量不能被赋值,无论是在非严格模式下还是在严格模式下,都不能对const声明的变量进行赋值。如下例:

const name = "eveningwater";
//错误
name = "loho";

不过,如果定义的是一个对象,可以对对象的值进行修改,如下例:

const student = {
    name:"eveningwater"
}
student.name = "loho";//没问题
//错误,相当于赋值修改对象
student = {
   name:"loho"
}
4.临时死区

前面提到letconst声明的变量都不会提升到作用域的顶部,因此在使用这两个标识符声明之前访问会报错,即使是typeof操作符也会触发引用错误。如下例:

console.log(typeof name);//报错
const name = "eveningwater";

由于第一行代码就报错了,因此后续的声明变量语句不会执行,此时就出现了JavaScript社区所谓的"临时死区"(temporal dead zone).虽然这里示例是const声明,但let声明也是一样的。

但如果在constlet声明的变量的作用域之外使用typeof操作符监测却不会报错,只不过会返回undefined。如下例:

console.log(typeof name);//undefined
if(condition){
   let name = "eveningwater";
}
5.循环中的块级作用域绑定

我们在使用var声明变量的时候,总会遇到这样的情况,如下:

for(var i = 0;i < 100;i++){
    //执行某些操作
}
//这里也能访问到变量i
console.log(i);//100

我们可以使用let声明将变量i限制在循环中,此时再在循环作用域之外访问变量i就会报错了,因为let声明已经为循环创建了一个块级作用域。如下:

for(let i = 0;i < 100;i++){
    //执行某些操作
}
//报错
console.log(i);
6.循环中的创建函数

在使用var声明变量的循环中,创建一个函数非常的困难,如下例:

var func = [];
for(var i = 0;i < 5;i++){
    func.push(function(){
        console.log(i);
    })
}
func.forEach(function(func){
    func();
});

你可能预期想的是打印从0到5之间,即0,1,2,3,4的数字,但实际上答案并不是如此。由于函数有自己的作用域,因此在向数组中添加函数的时候,实际上循环已经运行完成,因此每次打印变量i的值都相当于是在全局中访问变量i的值,即i = 5这个值,因此实际上答案最终会返回5次5.

es5中,我们可以使用函数表达式(IIFE)来解决这个问题,因为函数表达式会创建一个自己的块级作用域。如下:

var func = [];
for(var i = 0;i < 5;i++){
    (function(i){
       func.push(function(){
           console.log(i);
       })
    })(i)
}
func.forEach(function(func){
    func();//这就是你想要的答案,输出0,1,2,3,4
})

;

但事实上有了es6let声明,我们不必如此麻烦,只需要将var变成let声明就可以了,如下:

var func = [];
for(let i = 0;i < 5;i++){
    func.push(function(){
        console.log(i);
    })
}
func.forEach(function(func){
    func();//输出0,1,2,3,4
})

但是这里不能使用const声明,因为前面提到过,const声明并初始化了一个常量之后是不能被修改的,只能在对象中被修改值。如以下示例就会报错:

//在执行循环i++条件的时候就会报错
for(const i = 0;i < len;i++){
    console.log(i);
}

因为i++这个语句就是在尝试修改常量i的值,因此不能将const声明用在for循环中,但可以将const声明用在for-in或者for-of循环中。如下:

var func = [];
var obj = {
    name:"eveningwater",
    age:22
}
for(let key in obj){
  func.push(function(){
      console.log(key)
  })
}
func.forEach(function(func){
    func();//name,age
});
//以下也没问题
var func = [];
var obj = {
    name:"eveningwater",
    age:22
}
for(const key in obj){
    func.push(function(){
       console.log(key)
    })
}
func.forEach(function(func){
  func();//name,age
});

这里并没有修改key的值,因此使用constlet声明都可以,同理for-of循环也是一样的道理。for-of循环是es6的新增的循坏。。

7.全局作用域绑定

let,const声明与var声明还有一个区别就是三者在全局作用域中的行为。当使用var声明一个变量时,会在全局作用域(通常情况下是浏览器window对象)中创建一个全局属性,这也就意味着可能会覆盖window对象中已经存在的一个全局变量。如下例:

console.log(window.Array);//应该返回创建数组的构造函数,即f Array(){}
var Array = "这是数组";
console.log(window.Array);//返回"这是数组";

从上例,我们可以知道即使全局作用域中已经定义了Array变量或者已经存在了Array属性,但我们之后定义的Array变量则会覆盖之前已经定义好的或者已经存在的Array变量(属性)。

但是es6letconst声明则不会出现这种情况,letconst声明会创建一个新的绑定,也就是说不会成为window对象的属性。换句话说,就是所声明的变量不会覆盖全局变量,而只会遮蔽它。如下例:

let Array = "这是数组";
console.log(Array);//"这是数组‘;
console.log(window.Array);//应该返回创建数组的构造函数,即f Array(){}

这也就是说window.Array !== Array这个等式返回布尔值true

8.块级绑定的最佳实践

在使用es6块级声明变量中,最佳实践是如果确定后续不会改变这个变量的值,用const声明,如果确定要改变这个变量的值,则用let声明。因为预料外的变量值的改变时很多bug出现的源头。如下示例:

function  eveningWater(){};
eveningWater.prototype.name = "eveningwater";
let ew = new eveningWater();
//定义不能被修改的变量,也就是用于判断实例类型的属性
const _constructor = ew.constructor;
//可以改变自定义的名字属性
let  name = ew.name;
if(_constructor ===  eveningWater || _constuctor === Object){
  console.log(_constructor);
}else{
   name = "loho";
   console.log(name)
}

鄙人创建了一个QQ群,供大家学习交流,希望和大家合作愉快,互相帮助,交流学习,以下为群二维码:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102907.html

相关文章

  • ES6学习 第一章 let 和 const 命令

    摘要:外层作用域不报错正常输出块级作用域与函数声明规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。规定,块级作用域之中,函数声明语句的行为类似于,在块级作用域之外不可引用。同时,函数声明还会提升到所在的块级作用域的头部。 前言:最近开始看阮一峰老师的《ECMAScript 6 入门》(以下简称原...

    番茄西红柿 评论0 收藏2637
  • es6 - 块级作用

    摘要:和块级作用域在高程中,作者强调说没有块级作用域。然而,这种情况在中发生了改变,通过在代码块中使用引入了块级作用域的特性。在块级作用域内部使用声明的变量,在块作用域外部是不可见的。 let const和块级作用域 在js高程中,作者强调说js没有块级作用域。然而,这种情况在es6中发生了改变,es6通过在代码块中使用let, const引入了块级作用域的特性。下面是对此特性的介绍。 语法...

    wuaiqiu 评论0 收藏0
  • ES6学习摘要(01)(新人学习)

    摘要:入门一前言由于最近本人在学习,做一些笔记能够更好的熟悉,就趁此机会来写一篇关于的新人学习摘要吧。的作用域与命令相同只在声明所在的块级作用域内有效。块级作用域新增方式和实际上为新增了块级作用域。同时,函数声明还会提升到所在的块级作用域的头部。 ECMAScript6/ES6 入门 一、前言 由于最近本人在学习ES6,做一些笔记能够更好的熟悉,就趁此机会来写一篇关于ES6的新人学习摘要吧。...

    dmlllll 评论0 收藏0
  • es6学习之let和const命令

    摘要:和命令命令是在它所在的代码块有效,它属于块级作用域,新增。只有全局作用域和函数作用域。 let和const命令 let命令是在它所在的代码块有效,它属于块级作用域,es6新增。es5只有全局作用域和函数作用域。let命令存在暂时性死区(TDZ),即在申明前使用就会报错,不存在变量提升 console.log(a); // 报错 let a = 111; ==let不允许在相同作用域中,...

    DrizzleX 评论0 收藏0
  • ES5和ES6作用详解

    摘要:允许在块级作用域内声明函数。上面代码中,存在全局变量,但是块级作用域内又声明了一个局部变量,导致后者绑定这个块级作用域,所以在声明变量前,对赋值会报错。 ES5的作用域 变量起作用的范围,js中能创建作用域的只能是函数 { let a = 1; var b = 2; } console.log(a); // a is not defined console.log(b); //...

    Dr_Noooo 评论0 收藏0
  • ES6精解:let、const、块级作用

    摘要:命令新增了命令,跟类似,都是用来声明变量的不允许重复声明报错不存在变量提升报错正确写法为既要先定义,后面才能有这个值,否则会报错,如果改成会提示未定义,但是就直接报错了暂时性死区只要在块级作用域里面存在则它所声明的变量就绑定在这个块级作用域 let命令 ES6新增了let命令,跟var类似,都是用来声明变量的 1.不允许重复声明 { let a = 1; let a =...

    BWrong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<