前言:
最近开始看 阮一峰老师的《ECMAScript 6 入门》(以下简称原文)学习 ECMAScript 6(下文简称 ES6)的常识,整顿出一些知识点加上我的了解来做成文章笔记。依照章节为单位一个章节一篇笔记。
文章代码与目录构造和原文不同。
这一章原文链接 let 和 const 命令。
let
let
是用来申明一个 变量 。
不同与 var
会存在变量晋升 (下文有介绍),let
所申明的变量值只在 let
命令所在的代码块内无效。
同一个作用域 (下文有介绍) 不可应用 let
反复申明同一个变量。
留神:
- 申明变量
- 没有变量晋升
- 不可反复申明
- 只在
let
命令所在代码块无效
let sample = 1;
sample = 2;
let sample = 2; // 将会报错
{
let sample = 1;
console.log(sample); // 失常输入 1
}
console.log(sample); // 将会报错,因为只在 let 命令所在代码块无效
const
const
是用来申明一个 只读常量 。
一旦申明,常量的值就不能扭转。如果试着扭转常量的值会报错。
并且 const
在申明的时候就必须对其赋值,只申明不赋值,也会报错。
同一个作用域不可应用 const
反复申明同一个常量。const
与 let
一样,都因为作用域起因,只能在所在代码块中无效。
const 实际上保障的,并不是变量的值不得改变,而是变量指向的那个内存地址所保留的数据不得改变。
留神:
- 申明常量
- 申明后不能够扭转
- 申明的时候必须对其赋值
- 不可反复申明
- 在
const
命令所在代码块无效
const sample = 1;
sample = 2; // 将会报错,const 申明的变量不能够从新赋值
const sample; // 间接报错,const 申明的时候必须对其赋值
let 与 const
引入 let
后,曾经能够代替 var
了,在 let
与const
之中能用 const
就尽量用const
。
let 与 const 不同处
let
与 const
的区别就是一个申明变量一个申明常量,变量能够从新赋值,常量不能从新赋值。
let sampleLet = 2;
const sampleConst = 1;
sampleLet = 3; // 失常
sampleConst = 3; // 报错
let 与 const 雷同处
- 都只能先申明后应用,不能变量晋升。
- 都不能够在同一个作用域中反复申明
- 都只在命令所在代码块无效
{
sampleLet; // 报错
sampleConst; // 报错
let sampleLet = 2;
const sampleConst = 1;
let sampleLet = 3; // 报错
const sampleConst = 3; // 报错
}
sampleLet; // 报错
sampleConst; // 报错
变量晋升(Hoisting)
在 ES6 之前,应用 var
申明变量时会产生一种叫做变量晋升的个性。
无论是在代码的哪个中央申明的,都会晋升到以后作用域的最顶部,这种行为叫做变量晋升。
为了纠正这种景象,let
命令扭转了语法行为,它所申明的变量肯定要在申明后应用,否则报错
上文
let
与const
示意变量不能晋升,真的是这样吗?
其实在 JavaScript 中,所有示意var, let, const, function, function*, class
的申明都会被晋升。let
与const
申明变量会在环境实例化时被创立,然而在变量的词法绑定之前不容许以任何形式对其进行拜访,也就是说,当你在申明前调用变量将会报错然而报错信息不是未定义而是无奈在初始化之前拜访。这里也就引出了下一个概念,叫做 暂时性死区。
// var 申明会变量晋升,不会报错,然而值为 undefined
console.log(sampleVar); // undefined
var sampleVar = 1;
// let 申明不会变量晋升,然而报错不是 not defined
console.log(sampleLet); // Cannot access 'sampleLet' before initialization
let sampleLet = 1;
// const 申明不会变量晋升,然而报错不是 not defined
console.log(sampleConst); // Cannot access 'sampleConst' before initialization
const sampleConst = 1;
// 间接应用没有申明的变量报错为”is not defined“console.log(sample); //sample is not defined
暂时性死区
ES6 规定,如果代码区块中存在 let
和 const
命令申明的变量,这个区块对这些变量从一开始就造成了关闭作用域,但凡在申明之前就应用这些变量,就会报错。直到申明语句实现,这些变量能力被拜访(获取或设置),
这在语法上称为“暂时性死区”(英 temporal dead zone,简 TDZ),即代码块开始到变量申明语句实现之间的区域。
var sample = 1;
if (true) {
sample = '1'; // 报错
let sample;
}
简略来说,就是let
和 const
命令申明的变量,在进入这个申明代码所在的作用域时,就曾经存在,然而不能够获取或应用,直到申明语句实现,才能够拜访。
块级作用域
作用域(scope,或译无效范畴)就是变量和函数的可拜访范畴,即作用域管制着变量和函数的可见性和生命周期。
let 与 const 块级作用域
作用域并不是 ES6 的新货色,然而在 ES5 只有全局作用域和函数作用域,为了解决块级作用域,ES6 能够应用 **let**
与**const**
申明一个块级作用域的变量。var
申明的变量具备变量晋升个性,所以没有块的概念,能够跨块拜访,但不能跨函数。
外层作用域无奈读取内层作用域的变量。
{ // 块作用域
var sampleVar = 1;
let sampleLet = 2;
const sampleConst = 3;
console.log(sampleVar); // 胜利输入 1
console.log(sampleLet); // 胜利输入 2
console.log(sampleConst); // 胜利输入 3
}
console.log(sampleVar); // 胜利输入 1
console.log(sampleLet); // 报错 not defined
console.log(sampleConst); // 报错 not defined
ES6 容许块级作用域的任意嵌套。
同一个作用域不可应用 let
或const
申明同一个变量,内层作用域能够定义外层作用域的同名变量。
{
{
{
let sample = 'Hello World'; // 外层作用域
{let sample = 'sample';} // 不报错
{console.log(sample); } // 失常输入‘Hello World’}
}
}
块级作用域与函数申明
ES5 规定,函数只能在顶层作用域和函数作用域之中申明,不能在块级作用域申明。
ES6 规定,块级作用域之中,函数申明语句的行为相似于**let**
,在块级作用域之外不可援用。
/*
ES5,这两种状况都是不非法的,因为这两个函数申明都是在块作用域中申明。但应为浏览器为了兼容以前的旧代码,还是反对在块级作用域之中申明函数。所以不会报错
*/
if (true) {function sampleFn() {}}
try {function sampleFn() {}} catch(e) {// ...}
/*
ES6,函数申明语句的行为相似于 let,在块级作用域之外不可援用
*/
if (true) {sampleFn(); // 失常输入,函数申明语句的行为相似于 let
function sampleFn() {console.log('Hello World');
}
}
// 但其实在块级作用域之外也能够援用函数,只不过值为 undefined
if (false) {function sampleFn() {console.log('Hello World'); }
}
console.log(sampleFn); // 失常输入 undefined
sampleFn(); // 报错为 sampleFdddn is not defined
为什么块级作用域之外也能够援用函数呢?
如果扭转了块级作用域内申明的函数的解决规定,显然会对老代码产生很大影响。为了加重因而产生的不兼容问题,ES6 在附录 B 外面规定,浏览器的实现能够不恪守下面的规定(指函数申明语句的行为),有本人的行为形式。
- 容许在块级作用域内申明函数。
- 函数申明相似于
**var**
,即会晋升到全局作用域或函数作用域的头部。 - 同时,函数申明还会晋升到所在的块级作用域的头部。
留神 ,下面三条规定只对 ES6 的浏览器实现无效,其余环境的实现不必恪守,还是将块级作用域的函数申明当作let
解决。
咱们应该防止在块级作用域内申明函数。如果的确须要,也应该写成函数表达式,而不是函数申明语句。
// 函数申明语句,不要在块作用域中应用,因为会有变量晋升
{function sampleFn() {console.log("Hello World");
}
}
// 函数表达式,在块作用域中,函数不会有变量晋升
{const sampleFn = function () {console.log("Hello World");
}
}
顶层对象
顶层对象,在浏览器环境指的是 window
对象。
ES5 之中,顶层对象的属性与全局变量是等价的。
ES6 为了扭转这一点,
一方面规定,为了放弃兼容性,var
命令和 function
命令申明的全局变量,仍旧是顶层对象的属性;
另一方面规定,let
命令、const
命令、class
命令申明的全局变量,不属于顶层对象的属性。
/*
ES5 之中,顶层对象的属性赋值与全局变量的赋值,是同一件事。*/
window.sample = 1;
console.log(window.sample); // 失常输入 1
sample = 2;
console.log(window.sample);// 失常输入 2
/*
ES6 之中,let 命令、const 命令、class 命令申明的全局变量,不属于顶层对象的属性。*/
var sampleVar = 1;
console.log(window.sampleVar) // 失常输入 1
let sampleLet = 1;
console.log(window.sampleLet) // 失常输入 undefined
let sampleConst = 1;
console.log(window.sampleConst) // 失常输入 undefined
window
提供全局环境(即全局作用域)所有代码都是在这个环境中运行。
函数外面的 this
,如果函数不是作为对象的办法运行,而是单纯作为函数运行,this
会指向顶层对象。然而,严格模式下,这时 this
会返回 undefined
。
不论是严格模式,还是一般模式,new Function('return this')()
,总是会返回全局对象。
function sampleFn(){console.log(this);
}
sampleFn(); // 失常输入 输入全局对象 window
function sampleFn1(){
"use strict";
console.log(this)
}
sampleFn1(); // 失常输入 undefined
// 开启严格模式
"use strict";
const sample = new Function('return this')();
console.log(sample); // 失常输入 输入全局对象 window