一、let const和var

1.1 var依赖于window/this对象,而let和const不会

let和const是ES6提出的新语法,主要解决使用var导致的全局作用域问题。
在全局作用域中,this引用会指向window对象或全局语境的实例

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <title>1</title>
  7. </head>
  8. <body>
  9. <script>
  10. console.log(this===window);//true
  11. var c = "c"; //依附于window(全局作用域的this)
  12. let d = "d"; //独立于this
  13. console.log(c); //c
  14. console.log(this.c);//c
  15. console.log(window.c);//c
  16. console.log(d);//d
  17. console.log(this.d);//undefined
  18. console.log(window.d);//undefined
  19. </script>
  20. </body>
  21. </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 的模块化语法,可以按照 如下两个步骤进行配置:
  1. 确保安装了 v14.15.1 或更高版本的 node.js
  2. 在 package.json 的根节点中添加 “type“: “module“ 节点

在文件夹下,进入终端,会在文件夹下生成一个pakage.json文件

yarn init -y
#或
npm init -y

image.png
文件内容为:

{
  "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

结果可见n2 未进行导出
image.png

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)

执行”04.按需导入.js”文件
image.png

2.3.2.3 注意事项
  • 每个模块中可以使用多次按需导出
  • 按需导入的成员名称必须和按需导出的名称保持一致
  • 按需导入时,可以使用 as 关键字进行重命名
  • 按需导入可以和默认导入一起使用

    2.3.3 直接导入并执行模块中的代码

    如果只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码。
    新建”05.直接运行模块中的代码.js”文件
    for (let i = 0; i < 3; i++) {
    console.log(i)
    }
    
    新建test.js文件,并运行
    import './05.直接运行模块中的代码.js'
    
    image.png

    三、模板字符串

    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);//捕捉事件
  1. 无this绑定:箭头函数并不绑定this关键字,它从外部作用域中查找this的值,这与其他的变量一样。因此可以说箭头函数拥有“透明”的作用域。
  2. 无arguments对象:在箭头函数作用域中并不存在arguments对象,如果试图访问该对象,将会发生引用错误
  3. 无构造函数: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() 方法时,成功的回调函数是必选的、失败的回调函数是可选

promises.png

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)
  })