资讯专栏INFORMATION COLUMN

ES6-新的变量声明方式(2)

makeFoxPlay / 2893人阅读

摘要:在中,变量声明只有和以及隐式声明三种,在中则增加了和四种。用声明的变量只作用于循环体内部,不受外界干扰。不允许重复声明在相同的作用域内,用声明变量时,只允许声明一遍,但是可以多次声明的。

在ES5中,变量声明只有var和function以及隐式声明三种,在ES6中则增加了let、const、import和class四种。

1. let

1.1 块级作用域

let声明的变量的作用域是块级作用域(这个特性有点类似于后台语言),ES5 并没有块级作用域,只有函数作用域和全局作用域。

{
  let a = "ES6";
  var b = "ES5";
}

console.log(b)  // ES5 
console.log(a)  // ReferenceError: a is not defined.

那么let的块级作用域有什么好处呢?

let非常适合用于 for循环内部的块级作用域。JS中的for循环体比较特殊,每次执行都是一个全新的独立的块作用域,用let声明的变量传入到 for循环体的作用域后,不会发生改变,不受外界的影响。看一个常见的面试题目:

for (var i = 0; i <10; i++) {  
  setTimeout(function() {  // 同步注册回调函数到异步的宏任务队列。
    console.log(i);        // 执行此代码时,同步代码for循环已经执行完成
  }, 0);
}
// 输出结果
10   (共10个)
// 这里变量为i的for循环中,i是一个全局变量,在全局范围内都有效,所以每一次循环,新的i值都会覆盖旧值,导致最后输出的是最后一轮i的值,即i的最终结果为10,实际上都是console.log(10)。涉及的知识点:JS的事件循环机制,setTimeout的机制等

把 var改成 let声明:

for (let i = 0; i < 10; i++) { 
  setTimeout(function() {
    console.log(i);    //当前的i仅在本轮的循环中有效,就是说每一次循环,i其实都是一个新产生的变量。                          //用 let 声明的变量 i 只作用于循环体内部,不受外界干扰。
  }, 0);
}
// 输出结果:
0  1  2  3  4  5  6  7  8 9



1.2 暂时性死区(Temporal Dead Zone)

在一个块级作用域中,变量唯一存在,一旦在块级作用域中用let声明了一个变量,那么这个变量就唯一属于这个块级作用域,不受外部变量的影响,如下面所示。

var tmp = "bread and dream";
if(true){
    tmp = "dream or bread";   //ReferenceError
    let tmp;
}

这个例子中tmp = "dream or bread"的赋值会报错,因为在if块中的let对tmp变量进行了声明,导致该tmp绑定了这个作用域,而let临时死区导致了并不能在声明前使用,所以在声明前对变量赋值会报错。

暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

暂时性死区的意义也是让我们标准化代码,将所有变量的声明放在作用域的最开始。

1.3 不允许重复声明

(1) 在相同的作用域内,用let声明变量时,只允许声明一遍,但 var是可以多次声明的。大家都知道ES5 多次声明会造成变量覆盖而且不会报错,这就给调试增加了难度,而let能够直接扼杀这个问题在摇篮之中,因为会直接报错。

// 不报错
function demo() {
  var a = "bread and dream";
  var a = "dream or bread";
}
 
// 报错,Duplicate declaration "a"
function demo() {
  let a = "bread and dream";
  var a = "dream or bread";
}

// 报错,Duplicate declaration "a"
function demo() {
  let a = "bread and dream";
  let a = "dream or bread";
}

(2) 不能在函数内部重新声明参数:

function demo1(arg) {
  let arg; // 报错
}
demo1()
function demo2(arg) {
  {
    let arg; // 不报错
  }
}
demo2()


2. const

2.1 用于声明常量

const声明的常量是不允许改变的,只读属性,这意味常量声明时必须同时赋值, 只声明不赋值,就会报错,通常常量以大写字母命名。

const Person;   // 错误,必须初始化 
const Person = "bread and dream";// 正确

const Person2 = "no"; 
Person2 = "dream or bread"; //报错,不能重新赋值

这样做的两个好处:一是阅读代码的人立刻会意识到不应该修改这个值,二是防止了无意间修改变量值所导致的错误。比如我们使用nodejs的一些模块的时候,我们只是使用对应的模块(如http模块),但是并不需要修改nodejs的模块,这个时候就可以声明成const,增加了代码的可读性和避免错误。

2.2 支持块级作用域

const和let类似,也是支持块级作用域.

if (true) {
  const MIN = 5;
}

MIN // Uncaught ReferenceError: MIN is not defined


2.3 不支持变量提升,有暂时性死区

const声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。

if (true) {
  console.log(MIN); // ReferenceError
  const MIN = 5;
}


2.4 特殊情况

如果声明的常量是一个对象,那么对于对象本身是不允许重新赋值的,但是对于对象的属性是可以赋值的。

const obj = {};
obj.a = "xiao hua";
console.log(obj.a);    //"xiao hua"

实际上const能保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。

但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针。

至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

如果要彻底将对象冻结(不可修改其属性),应该使用Object.freeze(obj)方法。同理,数组也是一样的。

3. import

ES6采用import来代替node等的require来导入模块。

import {$} from "./jquery.js"  

$对象就是jquery中export暴露的对象。

如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。

import { JQ as $ } from "./jquery.js";

注意,import命令具有提升效果,会提升到整个模块的头部,首先执行。

4. class

ES6引入了类的概念,有了class这个关键字。类的实质还是函数对象。

先定义一个类:

//定义类
class Animal {
  constructor(name, age) {
        this.name = name;
        this.age = age;
  }    
  setSex(_sex) {
        this.sex=_sex;
  }
}  

constructor方法,就是构造方法,也就是ES5时代函数对象的主体,而this关键字则代表实例对象。

上面的类也可以改成ES5的写法:

function Animal(name, age){
        this.name = name;
        this.age = age;
}

Animal.prototype.setSex = function (_sex) {
        this.sex=_sex;
}

其实,大多数类的特性都可以通过之前的函数对象与原型来推导。

生成类的实例对象的写法,与ES5通过构造函数生成对象完全一样,也是使用new命令。

class Animal {}
let dog = new Animal();

在类的实例上面调用方法,其实就是调用原型上的方法,因为类上的方法其实都是添加在原型上。

Class其实就是一个function,但是有一点不同,Class不存在变量提升,也就是说Class声明定义必须在使用之前。

5.总结

    在ES6之前,JavaScript是没有块级作用域的,如果在块内使用var声明一个变量,它在代码块外面仍旧是可见的。ES6规范给开发者带来了块级作用域,let和const都添加了块级作用域,使得JS更严谨和规范。

let 与 const 相同点:

块级作用域
有暂时性死区 
约束了变量提升 
禁止重复声明变量 

let 与 const不同点:

const声明的变量不能重新赋值,也是由于这个规则,const变量声明时必须初始化,不能留到以后赋值。

合理的使用ES6新的声明方式,不管是面试还是工作中都有实际的应用,尤其是工作中,大家一定要尽量的多使用新的声明方式,不但可以让代码更规范,更可以避免不必要的bug,浪费调试时间,进而提高工作效率。

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

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

相关文章

  • ES6学习第一节 块级作用域的声明以及绑定 (一) let声明

    摘要:块级作用域绑定变量声明的变量声明与其他的一些计算机语言不同,在类的语言中,变量总是在被声明的地方创建而在中变量创建的位置取决于你如何声明它,为我们提供了新的声明方式用来让开发者能够更好的控制变量的作用域我们通过声明的变量,无论其声明位置在哪 块级作用域绑定 1.变量声明 JS的变量声明与其他的一些计算机语言不同,在类C的语言中,变量总是在被声明的地方创建;而在JS中变量创建的位置取决于...

    chanthuang 评论0 收藏0
  • ES6学习笔记--块级绑定

    摘要:对比常量声明与声明常量声明与声明,都是块级声明。最后一点全局块级绑定与不同于的另一个方面是在全局作用域上的表现。块级绑定新的最佳实践在的发展阶段,被广泛认可的变量声明方式是默认情况下应当使用而不是。总结与块级绑定将词法作用域引入。 var变量与变量提升 使用var关键字声明的变量,无论其实际声明位置在何处,都会被视为声明于所在函数的顶部(如果声明不在任意函数内,则被视为在全局作用域的顶...

    wangshijun 评论0 收藏0
  • ES6走走看看—由块级作用域引出的一场变革

    摘要:块级声明块级声明是用于声明在指定块的作用域之外无法访问的变量。美元符号可以放到任何一个位置,甚至单独一个美元符号。块级函数从开始,在严格模式下,块里的函数作用域为这个块。 持续更新的github笔记,链接地址:Front-End-Basics 此篇文章的笔记地址:字符到底发生了什么变化 ES6走走看看系列,特别鸣谢奇舞读书会~ 块级作用域又称词法作用域,存在于: 函数内部(...

    荆兆峰 评论0 收藏0
  • 让你的代码更简短,更整洁,更易读的ES6小技巧

    摘要:让你的代码更简短,更整洁,更易读的小技巧写在文章前面这篇文章翻译自文章就代码整洁方面对进行了总结。如果你正在使用的代码使用的语法,这个是你需要注意的事情。更多还提供了我们很多很多其他的方式来使我们的代码更简洁,更易读,以及更稳定。 让你的代码更简短,更整洁,更易读的ES6小技巧 写在文章前面 这篇文章翻译自ES6 tips and tricks to make your code cl...

    wpw 评论0 收藏0
  • ES6 系列之 let 和 const

    摘要:块级作用域存在于函数内部块中字符和之间的区域和块级声明用于声明在指定块的作用域之外无法访问的变量。和都是块级声明的一种。值得一提的是声明不允许修改绑定,但允许修改值。这意味着当用声明对象时没有问题报错临时死区临时死区,简写为。 块级作用域的出现 通过 var 声明的变量存在变量提升的特性: if (condition) { var value = 1; } console.lo...

    PascalXie 评论0 收藏0

发表评论

0条评论

makeFoxPlay

|高级讲师

TA的文章

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