一、let const和var
1.1 var依赖于window/this对象,而let和const不会
let和const是ES6提出的新语法,主要解决使用var导致的全局作用域问题。
在全局作用域中,this引用会指向window对象或全局语境的实例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>1</title>
</head>
<body>
<script>
console.log(this===window);//true
var c = "c"; //依附于window(全局作用域的this)
let d = "d"; //独立于this
console.log(c); //c
console.log(this.c);//c
console.log(window.c);//c
console.log(d);//d
console.log(this.d);//undefined
console.log(window.d);//undefined
</script>
</body>
</html>
1.2 变量提升
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>1</title>
</head>
<body>
<script>
let a ='global a';
let b="global b";
function x(){
console.log("x():global b = "+b);//x():global b = global b
console.log("x():global a = "+a);//Uncaught ReferenceError
let a =1 //不提升
}
x();
</script>
</body>
</html>
如果函数体内部使用let或const定义局部变量a,那么会发生Reference-Error错误。 如果在函数体内部使用了某个变量,他会先在函数体内部寻找这个变量,如果变量使用在变量定义之前则会报错。 如果函数体内未定义使用的变量,则程序会在函数体外寻找这个变量,变量提升。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>1</title>
</head>
<body>
<script>
let a ='global a';
let b="global b";
function x(){
let a =1 ;
console.log("x():global b = "+b);//x():global b = global b
console.log("x():global a = "+a);//x():global a = 1
}
x();
</script>
</body>
</html>
二、ES6模块化:
ES6 模块化规范是浏览器端与服务器端通用的模块化开发规范。它的出现极大的降低了前端开发者的模块化学 习成本,开发者不需再额外学习 AMD、CMD 或 CommonJS 等模块化规范。
2.1 含义:
ES6 模块化规范中定义:
- 每个 js 文件都是一个独立的模块
- 导入其它模块成员使用 import 关键字
- 向外共享模块成员使用 export 关键字
2.2 在 node.js 中体验 ES6 模块化
node.js 中默认仅支持 CommonJS 模块化规范,若想基于 node.js 体验与学习 ES6 的模块化语法,可以按照 如下两个步骤进行配置:
- 确保安装了 v14.15.1 或更高版本的 node.js
- 在 package.json 的根节点中添加 “type“: “module“ 节点
在文件夹下,进入终端,会在文件夹下生成一个pakage.json文件
yarn init -y
#或
npm init -y
文件内容为:
{
"name": "day1",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
增加一条”type“: “module“
{
"type":"module",
"name": "day1",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
2.3 ES6 模块化基本语法
2.3.1 默认导出和导入
2.3.1.1 默认导出
每个模块中,只允许使用唯一的一次 export default,否则会报错!
新建一个js文件,名字叫做”01.默认导出.js”
let n1 = 10
let n2 = 20
function show() {}
export default {
n1,
show
}
2.3.1.2 默认导入
默认导入时接受名称可以任意名称,只要是合法的成员名称即可
新建一个js文件,名字叫做”02.默认导入.js”
import m1 from './01.默认导出.js'
console.log(m1)
终端执行02.默认导入.js文件
node 02.默认导入.js
2.3.2 按需导出和导入
2.3.2.1 按需导出
新建文件”03.按需导出.js”
export let s1 = 'aaa'
export let s2 = 'ccc'
export function say() {}
//默认导出
export default {
a: 20
}
2.3.2.2 按需导入
新建文件”04.按需导入.js”
import info, { s1, s2 as str2, say } from './03.按需导出.js'
console.log(s1)
console.log(str2)
console.log(say)
// info 为默认导入
console.log(info)
2.3.2.3 注意事项
- 每个模块中可以使用多次按需导出
- 按需导入的成员名称必须和按需导出的名称保持一致
- 按需导入时,可以使用 as 关键字进行重命名
- 按需导入可以和默认导入一起使用
2.3.3 直接导入并执行模块中的代码
如果只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码。
新建”05.直接运行模块中的代码.js”文件
新建test.js文件,并运行for (let i = 0; i < 3; i++) { console.log(i) }
import './05.直接运行模块中的代码.js'
三、模板字符串
ES6新增了使用模板字面量定义字符串的能力。3.1 所见即所得
与使用单引号或双引号不同,模板字面量保留换行符、空格等内容,可以跨行定义字符串:
let myString1 = 'hello \nworld';//普通字符串需要加入换行符才能换行。
let myString2 = `hello
world`;//模板字符串不需要换行符,o后面有一个空格
console.log(myString1);
//hello
//world
console.log(myString1.length);//12
console.log(myString2);
//hello
//world
console.log(myString2.length);/12
3.2 字符串插值
可以使用字符串插值将变量写入模板字符串中。
let apples=10;
const str=`There are ${apples} apples in the basket.`;
console.log(str);//There are 10 apples in the basket.
3.3 JSON生成
反引号不能用来定义对象字面量的属性名(必须使用单引号或双引号)
JSON格式要求对象的属性名必须使用双引号包裹(反引号不会起任何作用,也不会报错)
let obj ={ `a`:1}; // Uncaught SyntaxError
let json1='{`a`:1}';//{`a`:1} 这个格式是错误的(反引号)
let json2='{a:1}';//格式错误:无引号
let json3="{`a`:1}";这个格式是错误的(反引号)
let json4="{'a':1}";//{`a`:1} 这个格式是错误的(单引号)
let json5='{"a":1}';//{"a":1} 正确('+双引号)
let json6 =`{"a":1}`;//{"a":1} 正确(`+双引号)
四、箭头函数
4.1 定义
ES6中创建函数可以不使用Function关键字进行定义,而使用箭头函数
:::info
//箭头函数的语法
( 参数 ) => { 代码块 };
:::
创建一个箭头函数并赋值,调用
//箭头函数的赋值
let fun1 = ()=>{ return 1 };
//箭头函数的调用
fun1();//1
4.2 无return的箭头函数
箭头函数一项新增特性就是可以不适用return关键字,可以使其有返回值
let fun2 =() => 1;
fun2();//1
let fun3 = e => e;
fun3(2);//2
let fun4 = e => e+1;
fun4(2);//3
let clicked = event =>console.log(event.target);//捕捉事件
- 无this绑定:箭头函数并不绑定this关键字,它从外部作用域中查找this的值,这与其他的变量一样。因此可以说箭头函数拥有“透明”的作用域。
- 无arguments对象:在箭头函数作用域中并不存在arguments对象,如果试图访问该对象,将会发生引用错误
- 无构造函数:ES5风格的函数就是对象的构造函数,可以创建和调用函数,还可以使用对象的构造函数来(和new运算符一起)实例化对象。这样一来,函数本身就变为了类定义。但是箭头函数不可以作为对象的构造函数,同样不可以被实例化。适合在Array.filter、Array.map、Array.reduce等方法中,用作事件回调函数或函数表达式。更适合函数式编程。
let a = () => { console.log(arguments)};//arguments未定义。
function f(){
console.log(arguments)
};
五、Promise
5.1回调地狱
多层回调函数的相互嵌套,形成回调地狱
setTimeout(() => {
//第一层回调函数
console.log('延时1秒后输出')
setTimeout(() => {
//第二层回调函数
console.log('延时2秒后输出')
setTimeout(() => {
//第三层回调函数
console.log('延时3秒后输出')
}, 3000)
}, 2000)
}, 1000)
回调地狱的缺点:
- 代码耦合性太强,牵一发而动全身,难以维护
-
5.2 为了解决回调地狱的问题,ES6(ECMAScript 2015)中新增了 Promise 的概念
5.2.1 Promise 的基本概念
① Promise 是一个构造函数
我们可以创建 Promise 的实例
const p = new Promise()
- new 出来的 Promise 实例对象,代表一个异步操作
② Promise.prototype 上包含一个 .then()
方法
- 每一次 new Promise() 构造函数得到的实例对象,
- 都可以通过原型链的方式访问到 .then() 方法,例如 p.then()
③ .then() 方法用来预先指定成功和失败的回调函数
- p.then(成功的回调函数,失败的回调函数)
- p.then(result => { }, error => { })
- 调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选
5.2.2基于回调函数按顺序读取文件内容
按顺序读取file文件夹下,1.txt,2.txt,3.txt的内容,使用yarn add fs
// 读取文件 1.txt
fs.readFile('./fles/1.txt', 'utf8', (err1, r1) => {
if (err1) return console.log(err1.messaage) //读取文件1失败
console.log(r1)//读取文件1成功
//读取文件2.txt
fs.readFile('./fles/2.txt', 'utf8', (err2, r2) => {
if (err2) return console.log(err2.messaage) //读取文件2失败
console.log(r2)//读取文件2成功
//读取文件3.txt
fs.readFile('./fles/3.txt', 'utf8', (err3, r3) => {
if (err3) return console.log(err3.messaage) //读取文件3失败
console.log(r3)//读取文件3成功
})
})
})
5.2.3 基于then-fs读取文件内容
由于 node.js 官方提供的 fs 模块仅支持以回调函数的方式读取文件,不支持 Promise 的调用方式。因此,需 要先运行如下的命令,安装 then-fs 这个第三方包,从而支持我们基于 Promise 的方式读取文件的内容:
yarn add then-fs
# npm
npm install then-fs
import thenFs from 'then-fs'
// .then() 中失败回调时可选的,可以被忽略
thenFs.readFile('./files/1.txt', 'utf8').then((r1) => {console.log(r1)},err1=>{console.log(err1.messaage)})
thenFs.readFile('./files/2.txt', 'utf8').then((r2) => {console.log(r2)},err2=>{console.log(err2.messaage)})
thenFs.readFile('./files/3.txt', 'utf8').then((r3) => {console.log(r3)},err3=>{console.log(err3.messaage)})
.then()方法的特性
如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通 过 .then() 方法的链式调用,就解决了回调地狱的问题
基于Promise按顺序读取文件内容
Promise 支持链式调用,从而来解决回调地狱的问题。
import thenFs from 'then-fs'
thenFs
.readFile('./files/1.txt', 'utf8')
.then((r1) => {
console.log(r1)
return thenFs.readFile('./files/2.txt', 'utf8')
})
.then((r2) => {
console.log(r2)
return thenFs.readFile('./files/3.txt', 'utf8')
})
.then((r3) => {
console.log(r3)
})
通过.catch捕捉错误
在 Promise 的链式操作中如果发生了错误,可以使用 Promise.prototype.catch 方法进行捕获和处理
import thenFs from 'then-fs'
thenFs
.readFile('./files/1.txt', 'utf8')
.then((r1) => {
console.log(r1)
return thenFs.readFile('./files/2.txt', 'utf8')
})
.then((r2) => {
console.log(r2)
return thenFs.readFile('./files/3.txt', 'utf8')
})
.then((r3) => {
console.log(r3)
})
.catch((err) => {
console.log(err.message)
})
如果不希望前面的错误导致后续的 .then 无法正常执行,则可以将 .catch 的调用提前,示例代码如下
import thenFs from 'then-fs'
thenFs
.readFile('./files/11.txt', 'utf8')
.catch((err) => {
console.log(err.message)
})
.then((r1) => {
console.log(r1)
return thenFs.readFile('./files/2.txt', 'utf8')
})
.then((r2) => {
console.log(r2)
return thenFs.readFile('./files/3.txt', 'utf8')
})
.then((r3) => {
console.log(r3)
})