ECMAScript 6 学习笔记:变量定义方法

栏目: JavaScript · 发布时间: 7年前

内容简介:let 声明的变量只在 let 命令所在的代码块内有效。如果在 let 命令所在代码块外调用,则会报错。let 命令的特性导致它非常适合用于 for 循环,每次循环的 i 都是不同的变量,但是每一轮循环都会记住上一轮循环的值,而 var 声明的全局变量,每次循环都是同一个变量:使用传统的 var 定义变量的方式,声明的变量会发生变量提升现象,即定义的变量在编译时被提到了代码的头部,具体表现为变量可以在声明之前使用,值为 undefined。而 let 纠正了这种行为,使用 let 声明的变量一定要在声明后使

ES6 声明变量的六种方法

  • var 和 function
  • let 和 const
  • import 和 class

let 命令

基本用法

let 声明的变量只在 let 命令所在的代码块内有效。如果在 let 命令所在代码块外调用,则会报错。

{
  let foo = 1;
  var bar = 2;
  console.log(foo); // 1
  console.log(bar); // 2
}
console.log(foo); // ReferenceError: foo is not defined
console.log(bar); // 2

let 命令的特性导致它非常适合用于 for 循环,每次循环的 i 都是不同的变量,但是每一轮循环都会记住上一轮循环的值,而 var 声明的全局变量,每次循环都是同一个变量:

// 使用 var 定义变量
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[1](); // 无论如何都是 10,因为这里的 i 是全局变量

// 使用 let 定义变量
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[1](); // 输出 1,尽管每次循环的 i 都是重新定义的,但是 for 会记住上一次 i 的值

变量提升

使用传统的 var 定义变量的方式,声明的变量会发生变量提升现象,即定义的变量在编译时被提到了代码的头部,具体表现为变量可以在声明之前使用,值为 undefined。而 let 纠正了这种行为,使用 let 声明的变量一定要在声明后使用,否则会报错。

// 存在变量提升
console.log(foo); // 输出 undefined
var foo = 1;

// 不存在变量提升
console.log(bar); // 报错 ReferenceError
let bar = 1;

暂时性死区

只要块级作用域内存在 let 命令,它所声明的变量就“绑定”这个区域,不再受到外部的影响。ES6 明确规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。这种行为在语法上称为“暂时性死区”。

var foo = 1;

{
  foo = 2; // 报错 ReferenceError
  let foo; // 由于 let 的存在,区块被绑定,变量无法提升,因此上面会报错
}

重复声明

let 不允许在相同作用域内,重复声明同一个变量

{
  let a = 1;
  let a = 2; // 报错
}

{
  let a = 1;
  {
    let a = 2; // 不报错
  }
}

块级作用域

ES5 只有全局作用域和函数作用域,没有块级作用域,这样可能导致多种问题:

  • 内层变量覆盖外层变量
var tmp = 1;

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 2;
  }
}

f(); // undefined,因为 if 代码块中的变量发生了变量提升,导致 console.log 无法读取外部变量
  • 计数循环变量泄露为全局变量,如上文介绍 for 循环时所提到的那样。

ES6 允许块级作用域的任意嵌套,外层作用域无法读取内层作用域的变量,内层作用域可以定义外层作用域的同名变量。

{
  {let insane = 'Hello World'}
  console.log(insane); // 报错,外层作用域无法读取内层作用域的变量
};

函数声明

ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于 let,在块级作用域之外不可引用。但是在浏览器的 ES6 环境中,块级作用域内声明的函数,行为类似于 var 声明的变量。考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。另外,还有一个需要注意的地方。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。

// 不报错
'use strict';
if (true) {
  function f() {}
}

// 没有大括号,报错
'use strict';
if (true) 
  function f() {}

const 命令

const 声明一个只读的常量,一旦声明,常量的值就不能改变,这意味着,const 一旦声明变量,就必须立即初始化,不能留到以后赋值。const 命令的语法特性与 let 命令相同,比如作用域、变量提升规则等。

const PI = 3.1415;
PI = 3; // TypeError: Assignment to constant variable.

const 命令保证的是变量指向的内存地址保存的数据不能改动,而对于对象和数组,const 只能保证指向对象和数组的指针不变,因此对于 const 声明的对象和数组是可以使用其内部方法添加数值的,但是无法指向另一个对象或者数组。

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

foo = {123}; // TypeError: "foo" is read-only

如果需要声明一个完全无法改动的对象,应该使用 Object.freeze 方法,将对象和对象的属性全部冻结:

// 使用递归函数将对象和对象的属性全部冻结
var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach((key, i) => {
    if (typeof obj[key] === 'object') {
      constantize(obj[key]);
    }
  });
};

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithms with JavaScript

Data Structures and Algorithms with JavaScript

Michael McMillan / O'Reilly Media / 2014-2-22 / USD 28.51

If you’re using JavaScript on the server-side, you need to implement classic data structures that conventional object-oriented programs (such as C# and Java) provide. This practical book shows you how......一起来看看 《Data Structures and Algorithms with JavaScript》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换