Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

es6-let和const #12

Open
wozien opened this issue Sep 15, 2020 · 0 comments
Open

es6-let和const #12

wozien opened this issue Sep 15, 2020 · 0 comments
Labels

Comments

@wozien
Copy link
Owner

wozien commented Sep 15, 2020

我们知道es5中 var 声明变量是作用于在全局或函数作用域,并且全局声明的变量还会作为 window 对象的属性。这样会增加程序错误的产生和不可控。

变量提升

在全局或者函数作用内用 var 声明的变量,在js预编译阶段会提升到当前作用域的顶部,这就是变量提升。如下代码:

function getValue(condition) {
  if (condition) {
    var value = 'red';
  } else {
    console.log(value); // undefined
  }
  console.log(value);  // undefined
}

以上代码相当于把变量value放在当前作用域先声明,所以才能在if块外和else块访问到,都会输出 undefined

function getValue(condition) {
  var value;
  if (condition) {
    value = 'red';
  } else {
    console.log(value); // undefined
  }
  console.log(value);  // undefined
}

let声明

从上面的可以看出 var 声明的变量没有块级作用域的概念,所以es6引入了let 声明,并绑定在当前的块作用域,块作用域外访问报错,如下:

function getValue(condition) {
  if (condition) {
    let value = 'red';
  } else {
    console.log(value); // ReferenceError: value is not defined
  }
  console.log(value); // ReferenceError: value is not defined
}

禁止重复声明。在同一个块中不能用 let 声明已经存在的标识符,否则会报错。如果是嵌套的作用域中重复声明,则不会报错。

var count = 30;
let count = 30;  // SyntaxError: Identifier 'count' has already been declared

if (true) {
  let count = 30;  // 不会报错
}

const声明

在es6引入const 来进行常量的声明。他和前面的 let 一样有块作用域绑定和不许重复声明的特性。但是const 必须要在声明的阶段进行初始化,而let 不用。

let count;   // 不会报错

const count;  //SyntaxError: Missing initializer in const declaration

const count = 30;  // 正确声明

const 声明的变量不能再赋值。如果变量是引用类型,可以修改对象的属性值,但不可以重新修改绑定的对象。

const count = 20;
count = 30;  // TypeError: Assignment to constant variable.

const obj = {};
obj.name = 'wozien';

临时死区

由于letconst 不存在变量提升,所以在声明前使用该变量会报错。因为在声明前,该变量存在于所谓的临时死区(TDZ)。

if (true) {
  console.log(typeof value);
  let value = 'red'; // ReferenceError: value is not defined
}

当变量声明后,就会从临时死区移出,后续可正常访问。注意的是,TDZ是针对当前的块作用域而言,所以如下可以正确运行:

console.log(typeof value); // undefiend
if (true) {
  let value = 'red';
}

在循环中的区别

var 声明的循环变量,会在循环后外部可正常访问,并且值为跳出循环的值。let 声明的变量则只在循环体内有效,如下:

for (var i = 0; i < 5; i++) {}
console.log(i); // 5

for (let i = 0; i < 5; i++) {}
console.log(i); // ReferenceError: i is not defined

在利用 var 声明的循环中创建函数会变得很艰难,因为函数执行的时候是迭代完的最终,如下:

const func = [];
for (var i = 0; i < 3; i++) {
  func.push(() => {
    console.log(i);
  });
}
func.forEach(func => func()); // 2 2 2

我们可以利用立即执行函数(IIFE)解决这个问题,让每个函数最终保存的是迭代过程中变量的副本。

for (var i = 0; i < 3; i++) {
  (function(value) {
    func.push(() => console.log(value));
  })(i);
}

在es6中循环里面 let 声明可以用来简化上面IIFE的实现过程,他会在每次迭代过程中重新声明一个同名变量i,值为当前的迭代i的值,所以循环体内的函数使用的都是i值的副本。

for (let i = 0; i < 3; i++) {
  func.push(() => console.log(i));
}
func.forEach(func => func()); // 0 1 2

如果把 let 改成 const, 在第二次迭代的时候会报错,因为 const 不许重新赋值。而对于 for-infor-of 循环两者都可以正常的运行。

const obj = {
  a: 1,
  b: 2,
  c: 3
};
const func = [];

for (let key in obj) {
  func.push(() => console.log(key));
}

func.forEach(func => func());  // a b c

如果把let 替换成 var ,将会输出3个c。因为 for-infor-of 每次都只会重新声明一个新的副本key。

在全局中的绑定

利用 var 在全局声明变量,会作为window对象的一个属性存在,而 letconst 则不会。

var a = 1;
let b = 2;
const c = 3;

console.log(window.a); // 1
console.log(window.b); // undefined
console.log(window.c); // undefined

小结

es6中的letconstvar 区别如下:

  • 绑定块作用域,不存在变量提升
  • 不允许重复定义
  • 声明前不允许使用变量
  • for 循环中每次创建新的副本
  • 全局声明不作为 window 属性

在我们平时的开发中,可以默认使用const。在确认需要改变变量的值时才使用let,可以一定程序上防止代码的错误产生。

@wozien wozien added the es6 label Sep 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant