es6之let和const命令的一些笔记

let和const命令

let命令

基本用法

let命令用来声明变量,声明的变量只在命令所在的代码块内有效。for循环中很适合使用let命令。

有必要理解的例子:

var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10

这里使用的var声明变量i,导致全局只有一个变量i,所以数组a的所有函数内部的console.log(i)里的i是最后一轮循环中i的值。也就是10,用let声明变量i能避免这个问题,使程序结果符合预期。

不存在变量提升

let不存在变量提升。使用var命令声明变量会发生变量提升,变量在声明前可以被使用,并且值为undefined。用let更符合我们的逻辑,用let声明的变量,要在声明之后才能使用,否则报错。

TDZ

在代码块内,使用let(或const)命令声明变量之前,该变量都是不可用的,在语法上,这称为"暂时性死区"(temporal dead zone)。

有必要理解的例子:

var tmp = 123;

if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError let tmp; // TDZ结束
console.log(tmp); // undefined tmp = 123;
console.log(tmp); // 123
}

未声明的变量,使用typeof不会报错,返回undefined。若是在let声明之前使用typeof则会报错。

关于TDZ,还有一些难以被发现的例子:

unction bar(x = y, y = 2) {
return [x, y];
} bar(); // 报错
//因为用y赋值给x,但此时y还没有声明,如果交换顺序则不会报错 function bar(x = 2, y = x) {
return [x, y];
}
bar(); // [2, 2] //···························
// 不报错
var x = x;
// 报错
let x = x;
// ReferenceError: x is not defined

这提示我们,我们应该避免在变量完成声明之前使用它,养成良好的编程习惯。

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

不允许重复声明

相同作用域内,let不允许重复声明一个对象。

块级作用域

如果没有块级作用域

ES5只有全局作用域和函数作用域,没有块级作用域。这会遇到一些问题。主要问题有2种:

  1. 内层变量覆盖外层变量。这是由于var声明的变量会发生变量提升,会导致一些容易忽视的问题。
  2. 在全局声明的变量在for循环之后依然存在。
//1.
var tmp = new Date(); function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
} f(); // undefined
//2.
var s = 'hello'; for (var i = 0; i < s.length; i++) {
console.log(s[i]);
} console.log(i); // 5

ES6的块级作用域

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

块级作用域与函数声明

避免在块级作用域内声明函数,因为环境的差异很大,会造出意想不到的效果。

es6的块级作用域允许声明函数的规则,只在使用大括号的情况下成立。否则报错。

do表达式

这个是提案中的语法。

const命令

基本用法

const用于声明只读的常量,不允许更改其的值。

同时意味着,用const声明变量需要在声明时初始化,后则报错。

const的作用域与let相同,只在声明的块级作用域中有效。

const声明的变量不发生变量提升,存在暂时性死区。

const和let都不可重复声明同一个变量。

本质

const保证变量指向的内存地址不得改动,而不是变量的值不得改动。所以对于复合类型的数据(如对象和数组),const变量保存的是指针,不能保证它指向的数据结构是不是可变的。

所以,我们声明一个对象为常量,仍然可以为它添加属性,不过不能让它指向另外一个对象。

如果真的想将对象冻结,应该使用Object.freeze方法。

严格模式下不能添加属性,会报错;非严格模式下,添加属性的语句不起作用。

ES6声明变量的6种方法

分别是var, function, let, const, import, class。

顶层对象的属性

在es6之前。顶层变量和全局变量是等价的。

例子:

window.a = 1;
a // 1 a = 2;
window.a // 2

es6为了改变这一设计缺陷,规定var和function声明的全局变量,依旧是顶层对象的属性。let, const, class命令声明的全局变量,不属于顶层对象的属性。

例子:

var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1 let b = 1;
window.b // undefined

global对象

不同运行环境下,JavaScript的global对象是不一致的。

我们可以使用this变量在取到顶层对象,但是也存在一些问题。

  1. 全局环境中,this会返回顶层对象。node模块和es6模块中,this返回当前模块。
  2. 函数里面的this,如果函数不是作为对象的方法允许,而是单独作为函数运行,this会指向顶层对象。但,严格模式下,this是undefined。
  3. 如果浏览器用了CSP,new Function('return this')()无法使用。
上一篇:浅谈js设计模式之代理模式


下一篇:SQL mysql优化