let 是一个用来声明变量的命令。它的用法相似于var ,可是所声明的变量,只在let 命令所在的代码块内有效。javascript
JavaScript引擎中,代码会在执行上下文中执行。而在执行上下文的建立阶段,声明的各类类型变量 (var
, let
, const
, function
, function*
, class
) 都会被添加到词法环境中。html
let
, const
, class
的「建立」过程被提高了,可是「初始化」没有提高。var
的「建立」过程被提高了,而且会被「初始化」为undefined。function
, function*
的「建立」「初始化」和「赋值」都被提高了。function
, function*
提高的优先级别更高,更早被添加到词法环境中。let
, const
, class
, function
, function*
被添加到词法环境中,而 var
被添加到变量环境中。var
变量绑定。使用for循环时,发现let居然能够重复定义同名变量:java
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
复制代码
总结:这代表函数内部的变量i 与for循环变量i 不在同一个做用域。循环变量的那部分是一个父做用域,而循环体内部是一个单独的子做用域。web
ES5 只有全局做用域和函数做用域,没有块级做用域,这带来不少不合理的场景。编程
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
复制代码
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
复制代码
上面代码中,变量i
只用来控制循环,可是循环结束后,它并无消失,泄露成了全局变量。数组
ES6 容许块级做用域的任意嵌套浏览器
ES6 的块级做用域必须有大括号markdown
内层做用域能够定义外层做用域的同名变量数据结构
{{{{
let insane = 'Hello World';
{let insane = 'Hello World'}
}}}};
复制代码
匿名当即执行函数表达式部分功能被替代模块化
// IIFE 写法
(function () {
var tmp = ...;
...
}());
// 块级做用域写法
{
let tmp = ...;
...
}
复制代码
容许在块级做用域之中声明函数
ES6 规定,块级做用域之中,函数声明语句的行为相似于let
,在块级做用域以外不可引用。
var
)。const
声明一个只读的常量。一旦声明,常量的值就不能改变。
只在声明所在的块级做用域内有效(与let
命令相同)。
const
命令声明的常量也是无初始化提高,一样存在暂时性死区。
const
声明的常量,也与let
同样不可重复声明。
const
仅仅保证指向的内存地址数据不得改动。
对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,所以等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const
只能保证这个指针是固定的(即老是指向另外一个固定的地址),至于它指向的数据结构则是可变的。
若是想冻结对象,应该使用Object.freeze
方法。
var
命令和function
命令是ES5 中仅有的两种声明变量的方法。let
和const
命令,import
命令和class
命令则是ES6新增的声明变量的方法。
window
对象,在 Node 指的是global
对象。var
命令和function
命令声明的全局变量,依旧是顶层对象的属性。let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。JavaScript 语言存在一个顶层对象,它提供全局环境(即全局做用域),全部代码都是在这个环境中运行。可是,顶层对象在各类实现里面是不统一的。
浏览器里面,顶层对象是window
,但 Node 和 Web Worker 没有window
。
浏览器和 Web Worker 里面,self
也指向顶层对象,可是 Node 没有self
。
Node 里面,顶层对象是global
,但其余环境都不支持。