目录
ES5语言有很多地方存在不合理以及不足,比如变量的提升、内置对象的方法不灵活再或者模块化实现不完善,需要借助art-template等模板引擎来完善和简化等等
ES6则是对ES5版本的升级完善,ES6不是单单的一个版本,它是ES6、ES7、ES8等统称,我们之后学习VUE、React以及Node.js运行环境都需要ES6的支持
一.let和const
ES6推出了两种新的声明变量的方法来代替var
我们对比一下这三种声明变量的方法,我们首先来看一下老版本ES5怎么声明以及它的缺点:
console.log(num);
// 打印
var num = 100;
// 声明变量
// 因为JS语法的预解析导致了变量的提升,我们的代码在执行前会被预解析为下面的代码:
var num;
console.log(num);
num = 100;
// 这样的话就存在一种不合理性了,本应该是获取不到值的而报错没有定义变量
// 可是我们会得到一个undefined,声明未赋值
1.let
a.阻止变量提升
为了解决使用 var 关键字声明变量所导致的变量提升,我们需要 ES6 的帮助,JavaScript 是从上往下执行的,本应该找不到变量而报错,却预解析为声明未赋值,这并不符合逻辑,在开发项目中也可能出现错误,所以ES6语法新增了let和const声明方法
这可有大学问了,ECMAScript2015明确规定,如果区块中存在 let 和 const 命令,那么这个区块对这些命令声明的变量从一开始就形成了一个封闭作用域,凡是在声明之前就使用这些变量就会报错未定义,找不到该变量,在代码块内使用 let 声明变量之前该变量不可用,在语法上被称为‘暂时性死区(Temporal dead zone)’,简称TDZ
我们来看一下let是怎么执行的:
console.log(num);
let num = 100;
//直接报错,找不到该变量
b.块级作用域
与 var 的最大区别就在这里,它们的作用域是不同的,如果使用 var 来声明变量,那么只有函数作用域和全局作用域,也就是说在块中定义的 var 变量在块外部同样可以访问,
而let会声明一个块级作用域,在块中声明的变量在块外是不能够访问的,好处就是它不会污染全局变量
//如果使用var声明变量:
if(100 == '100'){
var num = 100;
}
console.log(num);
//这时打印台会收到100值
//如果使用let声明变量:
if(100 == '100'){
let num = 100;
}
console.log(num);
//这时打印台会报错num is not defined,是找不到num变量的,因为此时num并不是全局变量
这里需要注意,虽然说它是一个块级作用域,但是不会影响作用域链以及函数的调用,依然会向上层寻求到变量值:
{
let num = 100;
function fn(){
console.log(num);
}
fn();
c.循环作用域
我们经常使用一些循环语句来达成业务需求,比如 for 循环 , for ... of 循环,for...in 循环,forEach循环,random循环累加,filter循环过滤,some循环等等等等,那么我们以 for 循环为例,let 在for循环中不仅 { } 会生成一个块级作用域,就连 ( ) 也会生成一个块级作用域,()块级作用域是 { } 块级作用域的父级作用域,我们在 ECMAScript2015 之前使用 var 声明的变量是全局作用域下的,当然包括在循环体内以及循环体外,而 let 声明的变量作用域只在循环体内,循环体外部无法使用该变量
var i = 1;
for (var i = 0; i < 10; i++) {}
console.log(i);
// 10
这明显不合理啊,我在全局声明的 i 变量为什么会被一个 for 循环改变了?🤔真让人头大
让我用 let 试试
let i = 1;
for (let i = 0; i < 2; i++) {
console.log(i);
}
console.log(i);
// 0 1 1
COOL!😆
关于 var 的神奇表现:
如果我给 for 循环中加上一个异步延迟:
var i = 1;
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 500);
}
// 3 3 3
为什么会出现这样的情况呢?我们都知道 setTimeout 是一个异步延迟函数,也就是说遇到 setTimeout 的时候会将它放到一个栈中,当时间到了之后才会被放到主执行线程进行执行,而这种方法正好遇到了 var ,var 在这里所声明的是全局变量,那么 for 循环会将该变量执行 for 循环操作并修改了全局变量 i 的值,所以当 setTimeout 回来的时候,打印出来的也只能是三遍一样的修改之后的字面量值
那么, let 呢? 可以解决这个问题吗?
let i = 1;
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 500);
}
// 0 1 2
COOL!😀
为什么呢?这很简单理解啊,因为在 ( ) 也是一个块级作用域,那么每一个循环值就会依次传入 setTimeout 异步执行函数中,不会造成一个污染的状况
等等!让你这么一说,ECMAScript2015 之前就没辙了吗?🤔其实并不是,办法总比困难多!
我们可以写一个闭包,也就是写一个立即执行函数来执行异步解决这个问题
var i = 1;
for (var i = 0; i < 3; i++) {
(function (_i) {
setTimeout(function () {
console.log(_i);
}, 500);
})(i);
}
// 0 1 2
其实最根本的问题还是解决了 var 的作用域问题,我们写了一个闭包,只是给到了 var 一个函数作用域
d.不能重复声明
在 ECMAScript2015 之前,如果是团队开发项目,如果我们声明了一个 var 变量,那么后面的同名变量会覆盖掉之前的字面量数据,这真的很不友好,极大的影响了团队合作开发的成本,而 let 却能极好的解决掉这个问题
//我们使用var声明来进行覆盖数据:
var num = 1;
var num = 100;
console.log(num);
//100
//我们使用let声明变量看一看能不能数据覆盖:
let num = 1;
let num = 100;
console.log(num);
//报错,num已经被声明了
好吧,都不需要打开控制台查看,VSCode已经给我们内部报错
2.const
const拥有let的特性,并且还不能被修改,它是一个只读变量,意思就是我们使用了const声明了一个变量的话,值是不可以被修改的 (在声明时必须赋上一个初始值)
建议:{在声明变量时,最好常量是大写字母},不过以我个人来说我常量一直使用驼峰命名法,变量使用 _ 分隔单词
//const声明变量并尝试修改:
const NUM = 1;
const NUM = 100;
console.log(NUM);
//报错:不能被修改
所以我们已经不常用或者不使用var来声明变量了,JS预解析的提升变量也对const和let所声明的变量无法控制了
tips:对数组或者对象的元素进行修改(例如向数组中添加一条数据),不算对常量的修改,const常量指向的地址还是不变的,要理解这个我们需要知道一个变量是怎么被声明到内存中的,这和堆栈有着千丝万缕的关系...对于复杂数据类型或者说引用类型,const 控制的不是值不变,而是限制变量名所指向的内存地址所保存的数据不发生改变
const ARR = ['giao','前端小蜗','喜欢前端'];
ARR.push('你也喜欢前端吗?');
二.模板字符串(模板字面量)
在 ECMAScript2015 之前字符串换行有以下俩种写法:
- 使用反斜杠
- 使用拼接字符串
ES6真的很强大,我们使用ES6的模板字符串(` `)以及变量占位符${}来做一段UI渲染:
//首先我们在HTML中创建一个div元素用来获取渲染内容
<div></div>
//获取到div元素
const BOX = document.querySelector('div');
//定义值以及模板
let name = "giao哥",age = 20;
let htmlStr = `<ul>
<li>
<a href="javascript:;">${name}<br>${age}</a>
</li>
</ul>`
//将模板数据整合,渲染到UI界面
BOX.innerHTML = htmlStr;
我们可以去尝试使用以下模板字面量占位符,里面还可以书写一行执行代码,非常的有趣😀,也就是说${}中相当于就是一个 JavaScript 环境,而不是单纯的字符串中变量的填充