学习笔记:ECMAScript教程(尚硅谷)

介绍

  • 本文主要记录在学习尚硅谷的 ECMAScript 课程时的一些笔记
  • 尚硅谷前端学科全套课程请点击这里进行下载,提取码:afyt

一、ECMA

  • ECMA-262 历史版本点击这里查看

  • ES6 兼容性点击这里查看

二、ES6

1.let

  • 变量不能重复声明

1
2
let name = "wrysmile"
let name = "Wrysmile"
  • 块级作用域

    • 在ES5中只有全局、函数、eval(严格模式下才有)作用域
    • ES6新增块级作用域,let 声明的变量只能作用在块级作用域中
    • if、else、while、for、花括号都属于块级作用域
  • 不存在变量提升

  • 不影响作用域链

!案例:遍历绑定事件时使用不使用this

  • 在遍历绑定事件时

    • 如果使用 var 声明,不使用 this 时会因为全局作用域中的变量导致事件找不到当前下标
    • 如果使用 let 声明,因为其声明的变量只在其块级作用域中有效,所以事件可以找到当前下标
  • 案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<h1>点击切换颜色</h1>
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<script>
let items = document.getElementsByClassName("item");
for (let i = 0; i < items.length; i++){
items[i].onclick = function (){
items[i].style.background = "red";
}
}
</script>

2.const

  • 作用:用来声明一个常量

  • 一定要赋初始值

  • 一般常量使用大写

  • 常量的值不能修改

  • 块级作用域

  • 对于数组和对象的元素修改,不算做常量的修改,不会报错

3.变量解构赋值

  • ES6 允许按照一定模式从数组和对象中提取值,对变量进行赋值,称为解构赋值

  • 数组的解构:

1
2
3
const arr = ["小明","小张","小王","小李"];
let [a,b,c,d] = arr;
console.log(a,b,c,d);
  • 对象的解构:

1
2
3
4
5
6
7
8
9
10
const obj = {
name: "Wrysmile",
age: 25,
eat: function (){
console.log("我爱吃西瓜");
}
}
let {name, age , eat} = obj;
console.log(name,age,eat);
eat();

4.模板字符串

  • 概念:ES6 中引入了新的声明字符串的方式

  • 内容中可以直接出现换行符

1
2
3
4
let str = `你好
你好
你好`;
console.log(str);
  • 支持变量拼接,只需要使用 ${} 即可进行拼接

1
2
3
let str1 = "陈奕迅";
let str2 = `${str1}是我最喜欢的歌手`;
console.log(str2);

5.简化对象写法

  • ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
let name = "Wrysmile";
let age = 25;
let change = function (){
console.log("改变");
}
const person = {
name,
age,
change,
improve(){
console.log("提高");
}
}

6.箭头函数

  • ES6 中允许使用箭头来定义函数

  • 特性:

    • this 是静态的,this 始终指向函数声明时所在作用域下的 this 的值
    • 不能作为构造函数来实例化对象
    • 不能使用 arguments 变量
  • 简写:

    • 省略小括号:当形参有且只有一个的时候
    • 省略花括号:当代码体只有一条语句的时候,此时代码体的 return 必须省略,因为语句的执行结果就是函数的返回值
  • 适合:

    • 与 this 无关的回调,如定时器,数组的方法回调
  • 不适合:

    • 与 this 有关的回调,如事件回调,对象的方法

案例

  • 通过静态this来修改元素属性

1
2
3
4
5
6
const ad = document.getElementById("ad");
ad.addEventListener("click", function (){
setTimeout(() => {
this.style.background = "pink";
},2000)
})
  • 从数组中返回偶数的元素

1
2
3
4
5
6
const arr = [1,4,6,7,58,63,100];
const newArr = arr.filter(function (item){
return item % 2 === 0;
})
const newArr2 = arr.filter(item => item %2 === 0);
console.log(newArr, newArr2);

7.函数参数的默认值

  • ES6 允许给函数参数赋值初始值

  • 特性:

    • 形参初始值,具有默认值的参数,一般位置要靠后
    • 可以与解构赋值结合
  • 与解构赋值结合可以省略一部分重复代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
function connect(options){
console.log(options.host, options.username, options.password, options.port);
}
function connect({host="127.0.0.1", username, password, port}){
console.log(host, username, password, port);
}
connect({
host: "www.wrysmile.cn",
username: "admin",
password: "admin",
port: 3306
})

8.rest 参数

  • ES6 可以使用 rest 参数来获取函数的实参,用来代替 arguments

  • 语法:...args,即在标识符前加三个点

1
2
3
4
5
6
7
function date(...args){
console.log(args);
}
function date(a, b, ...args){
console.log(args)
}
date(1, 2, 3, 9);
  • 与 arguments 的区别:

    • arguments 返回的是对象
    • rest 参数返回的是数组

9.扩展运算符

  • ... 能将数组转换为逗号分割的参数序列,如下:

1
2
3
4
5
6
const tfboys = ["易烊千玺", "王源", "王俊凯"];
function chunwan(){
console.log(arguments);
}
chunwan(tfboys);
chunwan(...tfboys);
  • 两个函数执行结果对比如下:
    es01.png

  • 应用:

    • 数组的合并
    • 数组的克隆
    • 伪数组转为真正的数组
  • 应用案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 数组的合并
const kuaizi = ["王太利", "肖央"];
const fenghuangchuanqi = ["曾毅", "玲花"];
const songer = [...kuaizi, ...fenghuangchuanqi];
console.log(songer);
// 数组的克隆
const sanzhihua = ["E","G","M"];
const sanyecao = [...sanzhihua];
console.log(sanyecao);
// 将伪数组转为真正的数组
const divs = document.querySelectorAll("div");
const divArr = [...divs]
console.log(divs);
console.log(divArr);

10.Symbol

  • ES6 中引入了一种新的原始数据类型 Symbol,表示独一无二的值

  • 它是 JS 的第七种数据类型,也是一种类似于字符串的数据类型

  • 特点:

    • Symbol 的值是唯一的,用来解决命名冲突的问题
    • Symbol 的值不能与其他数据进行运算
    • Symbol 定义的对象属性不能使用 for...in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名

(1).基础使用

  • 普通创建 Symbol:

1
let s = Symbol();
  • 使用 Symbol.for() 来创建 Symbol:(可以通过描述字符串来找到唯一的Symbol值)

1
let s1 = Symbol.for("s1");
  • 使用描述字符串来标识一个 Symbol:

1
let s2 = Symbol("s2");

(2).创建对象属性

  • 当向某个对象中添加方法时,为了避免重名的问题,可以使用 Symbol 来唯一标识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let game = {
name: "俄罗斯方块",
up: function (){
console.log("向上");
},
down: function (){
console.log("向下");
}
}
let methods = {
up: Symbol(),
down: Symbol()
}
game[methods.up] = function (){
console.log("唯一的向上");
}
game[methods.down] = function (){
console.log("唯一的向下");
}
console.log(game);

(3).Symbol 内置值

  • 除了定义自己使用的 Symbol 值以外,ES 6还提供了11个内置的 Symbol 值,指向语言内部使用的方法

  • 视频链接点这里的p17

11.迭代器

  • 迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署了 Iterator 接口就可以完成遍历操作

  • ES6 创造了一种新的遍历命令 for...of 循环,而迭代器主要供 for...of 消费

    • for...in 循环是获取到了属性名
    • fro...of 循环是获取到了属性值
  • 原生具备 Iterator 接口的数据,即可以使用 for...of 循环的有:Array、Arguments、Set、Map、String、TypedArray、NodeList

  • 工作原理:

    • 创建一个指针对象,指向当前数据结构的起始位置
    • 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
    • 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
    • 每调用 next 方法返回一个包含 value 和 done 属性的对象
1
2
3
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
let iterator = xiyou[Symbol.iterator]();
console.log(iterator.next());
  • 注意:需要自定义遍历数据的时候,要想到迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 自定义迭代器 —— 遍历得出对象中数组的每个值
const classNum = {
name: "云计算一班",
stus: [
'小明',
'小黄',
'小红'
],
[Symbol.iterator](){
// 索引变量
let index = 0;
// 保存this
let _this = this;
return {
next: function (){
if(index < _this.stus.length){
const result = {
value: _this.stus[index],
done: false
}
// 下标自增
index ++;
// 返回结果
return result;
}else {
return {
value: undefined,
done: true
}
}
}
}
}
}
for (let v of classNum){
console.log(v);
}

12.生成器

  • ES6 提供了一种异步编程的解决方案 —— 生成器函数

  • 异步编程指纯回调函数,例如:Node中的fs模块、ajax、MongoDB等

  • 创建生成器:在函数名前面加一个星号,返回的是迭代器对象,所以需要使用 next() 方法来执行其中的语句

1
2
3
4
5
function * gen(){
console.log("hello");
}
let iterator = gen();
iterator.next();
  • 代码分割:在该函数中可以使用 yield 来进行代码分割,执行 next() 方法时就会按分割的区域顺序执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function * gen(){
console.log("111");
yield "第一条分割线";
console.log("222");
yield "第二条分割线";
console.log("333");
yield "第三条分割线";
console.log("444");
}
let iterator = gen();
iterator.next();
iterator.next();
iterator.next();
iterator.next();
  • 函数传参:在 next() 方法中可以传入实参,该实参将作为上一次 yield 语句的返回值来进行展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function * gen2(arg){
console.log(arg);
let one = yield 111;
console.log(one);
let two = yield 222;
console.log(two);
let three = yield 333;
console.log(three);
}
let iterator2 = gen2("AAA");
console.log(iterator2.next());
console.log(iterator2.next("BBB"));
console.log(iterator2.next("CCC"));
console.log(iterator2.next("DDD"));

案例:

  • 1秒后输出111,2秒后输出222,3秒后输出333

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function one(){
setTimeout(() => {
console.log("111");
iterator.next();
}, 1000)
}
function two(){
setTimeout(() => {
console.log("222");
iterator.next();
}, 2000)
}
function three(){
setTimeout(() => {
console.log("333");
iterator.next();
}, 3000)
}
function * gen(){
yield one();
yield two();
yield three();
}
let iterator = gen();
iterator.next();
  • 模拟获取用户数据、订单数据、商品数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function getUser(){
setTimeout(() => {
let data = "用户数据";
iterator.next(data);
}, 1000)
}
function getOrder(){
setTimeout(() => {
let data = "订单数据";
iterator.next(data);
}, 1000)
}
function getGoods(){
setTimeout(() => {
let data = "商品数据";
iterator.next(data);
}, 1000)
}
function * gen(){
let user = yield getUser();
console.log(user);
let order = yield getOrder();
console.log(order);
let goods = yield getGoods();
console.log(goods);
}
let iterator = gen();
iterator.next();

13.Promise

  • ES6 引入的一种新的异步编程解决方案,是一个构造函数

  • 作用:封装异步操作并可以获取其成功或失败的结果

1
2
3
4
5
6
7
8
9
10
11
const p = new Promise(function (resolve, reject){
let data = "数据库中的数据";
// resolve(data);
let err = "错误的数据";
reject(err);
});
p.then(function (value){
console.log(value);
}, function (reason){
console.log(reason);
})
  • then() 方法:该方法的返回结构是 Promise 对象,对象状态由回调函数的执行结构来决定

    • 如果回调函数中返回的结构是非 Promise 类型的属性,返回状态为成功,返回值为对象的成功的值
    • 如果回调函数中返回的结构是 Promise 类型的属性,返回状态是成功则成功,失败则失败
    • 如果回调函数中返回的结构是抛出错误,返回状态为失败
    • 所以,then() 方法可以链式调用
  • catch() 方法:是 then() 方法的语法糖,具体同上

案例:

  • 使用 Promise 读取一个文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const fs = require("fs");
// 普通读取一个文件
fs.readFile("./file/test.md", (err,data) => {
if (err) throw err;
console.log(data.toString());
})
// Promise读取一个文件
const p = new Promise((resolve, reject) => {
fs.readFile("./file/test.md", (err, data) => {
if(err) reject(err);
resolve(data);
})
});
p.then((value) => {
console.log(value.toString());
}, (reason) => {
console.log(reason);
})
  • 使用 Promise 封装 AJAX:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:9000/data");
xhr.send();
xhr.onreadystatechange = function (){
if (xhr.readyState === 4){
if (xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response);
}else {
reject(xhr.response);
}
}
}
});
p.then(value => {
console.log(value);
}, reason => {
console.error(reason);
})
  • 使用 Promise 读取多个文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 普通读取多个文件
fs.readFile("./file/test1.md",(err,data1) => {
fs.readFile("./file/test2.md", (err,data2) => {
console.log(data1 + "\r\n" + data2);
})
})
// Promise读取多个文件
const p = new Promise((resolve, reject) => {
fs.readFile("./file/test1.md", (err,data) => {
resolve(data);
})
});
p.then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./file/test2.md", (err,data) => {
resolve([value, data]);
})
});
}).then(value => {
console.log(value.join("\r\n"));
})

14.Set

  • ES6 提供了新的数据结构 —— Set(集合),类似于数组但成员的值都是唯一的,数据类型是对象

  • 集合实现了 Iterator 接口,所以可以使用 扩展运算符for...of 来进行遍历

  • 声明一个 set,同时在传入值的时候还可以达到数组去重的效果

1
2
3
4
const s1 = new Set();
console.log(s1);
const s2 = new Set([1,2,3,4,3,2,1]);
console.log(s2)
  • 集合的属性和方法:

    • s.size():返回集合的元素个数
    • s.add():增加一个新元素,返回当前集合
    • s.delete():删除一各元素,返回布尔值
    • s.has():检测集合中是否包含某个元素,返回布尔值
    • s.clear():清空当前集合

案例:

  • 数组去重:

1
2
3
const arr = [1, 2, 4, 6, 4, 1];
const s = new Set(arr);
console.log([...s]);
  • 两数组的交集

1
2
3
4
5
6
7
8
9
10
11
12
13
const arr1 = [1, 2, 4, 6, 4, 1];
const arr2 = [5, 6, 6, 5];
const sArr1 = new Set(arr1);
const sArr2 = new Set(arr2);
const result = [...sArr1].filter(item => {
if (sArr2.has(item)){
return true;
}else {
return false;
}
})
const result1 = [...sArr1].filter(item => sArr2.has(item));
console.log(result, result1);
  • 两数组的并集

1
2
3
4
const arr1 = [1, 2, 4, 6, 4, 1];
const arr2 = [5, 6, 6, 5];
const result = [...new Set([...arr1,...arr2])];
console.log(result);
  • 两数组的差值

1
2
3
4
5
6
7
const arr1 = [1, 2, 4, 6, 4, 1];
const arr2 = [5, 6, 6, 5];
const sArr1 = new Set(arr1);
const sArr2 = new Set(arr2);
const se = [...sArr1].filter(item => !(sArr2.has(item)));
const desc = [...sArr2].filter(item => !(sArr1.has(item)));
console.log(se, desc);

15.Map

ES6 提供了 Map 数据结构,类似于对象,是键值对的集合,但键不限于字符串,各种类型的值(包括对象)都可以当做键

Map 也实现了 Iterator 接口,所以可以使用 扩展运算符for...of 来进行遍历

声明一个 Map:

1
2
3
const m = new Map();
m.set("name","Wrysmile");
console.log(m);

Map 的属性和方法:

  • m.size():返回 Map 的元素个数

  • m.set():增加一个新元素,返回当前 Map

  • m.get():返回键名对象的键值

  • m.has():检测 Map 中是否包含某个元素,返回布尔值

  • m.clear():清空集合,返回 undefined

16.Class

  • 通过 class 关键字可以定义类,可以看做一个语法糖

(1).声明类

  • 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Phone{
// 构造方法,语法固定不可修改
constructor(phone, price) {
this.phone = phone;
this.price = price;
}
// 不能使用 ES5 的对象完整形式
call(){
console.log("我可以打电话");
}
}
const onePlus = new Phone("一加",2999);
console.log(onePlus);

(2).静态成员

  • 通过 static 关键字设置的属性为静态属性,是不能被实例对象所引用的。就类似于 Phone.name 设置的属性

  • 而不通过 static 关键字设置的属性是可以被实例对象所调用的。就类似于 Phone.prototype.name 设置的属性

(3).继承

  • ES5 中的对象继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 父类
function Phone(brand, price){
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function (){
console.log("我可以打电话");
}
// 子类
function SmartPhone(brand, price, color, size){
Phone.call(this, brand, price);
this.color = color;
this.size = size;
}
// 设置子类构造函数的原型
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone;
// 声明子类的方法
SmartPhone.prototype.photo = function (){
console.log("我可以拍照片");
}
const onePlus = new SmartPhone("一加", 2999, "黑色", "6.6inch");
console.log(onePlus);
  • ES6 中的对象继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 父类
class Phone{
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
call(){
console.log("我可以打电话");
}
}
// 子类
class SmartPhone extends Phone{
constructor(brand, price, color, size) {
// 继承父类的属性
super(brand, price);
this.color = color;
this.size = size;
}
photo(){
console.log("我可以拍照片");
}
}
const onePlus = new SmartPhone("一加", 2999, "黑色", "6.6inch");
console.log(onePlus);

(4).重写

  • 我们可以在子类中重写父类的同名方法,同时也可以使用 super.方法名() 来调用父类的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 父类
class Phone{
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
call(){
console.log("我可以打电话");
}
}
// 子类
class SmartPhone extends Phone{
constructor(brand, price, color, size) {
// 继承父类的属性
super(brand, price);
this.color = color;
this.size = size;
}
call(){
super.call();
console.log("我是子类的打电话");
}
photo(){
console.log("我可以拍照片");
}
}
const onePlus = new SmartPhone("一加", 2999, "黑色", "6.6inch");
console.log(onePlus);

(5).get与set

  • 在 Class 中可以使用 get 来读取类中的方法,同时还可以读取到返回值

1
2
3
4
5
6
7
8
class Phone{
get call(){
console.log("call方法被调用了");
return "get到的";
}
}
const p = new Phone();
console.log(p.call);
  • 在 Class 中给方法赋值时就会调用 set 中的函数

1
2
3
4
5
6
7
class Phone{
set call(arg){
console.log("set中的方法被调用了");
}
}
const p = new Phone();
p.call = "flag";

17.数值扩展

  • Number.EPSILON:JS 中表示的最小精度,其值接近于 2.22*10(-16方)

  • 进制数定义:

    • 二进制:0b1010
    • 八进制:0o777
    • 十进制:100
    • 十六进制:0xff
  • Number.isFinite:检测一个数值是否为有限数

  • Number.isNaN:检测一个数值是否为 NaN

  • Number.parseInt/Float:将字符串转为整数/浮点数

  • Number.isInteger:判断一个数是否为整数

  • Math.trunc:将数字的小数部分抹掉

  • Math.sign:判断一个数到底为整数、负数还是零

18.对象方法扩展

  • Object.is:判断两个值是否完全相等

  • Object.assign(oldObject, newObject):对象的合并,当旧对象中有新对象中没有的时候,会进行合并,否则会直接覆盖

  • Object.setPrototypeOf:设置原型对象(不推荐使用)

  • Object.getPrototypeOf:获取原型对象(不推荐使用)

19.模块化

  • 是指将一个大的程序文件拆成许多小的文件,然后进行组合

  • 优势:

    • 防止命名冲突
    • 代码复用
    • 高维护性
  • 模块化规范产品:

    • CommonJS:NodeJS、Browserify
    • AMD:requireJS
    • CMD:seaJS

(1).暴露与引入

  • 暴露模块内容的方法:

    • 在每一个需要暴露的变量或方法前面都加一个 export
    • 也可以暴露一个对象,将需要暴露的变量或方法都放入对象中
    • 也可以使用 export default xxx 来进行默认暴露的设置
  • 通过 <script> 标签来引入模块的方法:

    • 通用的引入:import * as xxx from "./test.js"
    • 解构赋值的引入,以下三个分别对应暴露模块的三种形式:
      • import {school} from "./test.js"
      • import {school as guigu} from "./test.js"
      • import {default as a} from "./test.js"
    • 简便形式的引入,只针对于默认暴露的方法:import b from "./test.js"
  • 通过 <script> 标签的 src 属性来引入:

    • 在 src 中引入某个js文件
    • 然后在该 js 文件中直接使用上面语句进行引入

(2).babel代码转换

  • 通过使用 babel 可以将 ES6语法转换为 ES5语法,方便不同浏览器的使用,官网点击这里

  • 使用方法:

    • 使用 npm init --yes 进行初始化
    • 使用 npm i babel-cli babel-preset-env browserify -D 命令来安装相关的开发依赖,会安装babel-cli(命令行工具)、babel-preset-env(预设包,进行转换)、browserify(打包工具)这三个工具
    • 使用 npx babel 原始路径 -d 新路径 命令来进行转换
    • 使用 npm browserify 新路径下的配置文件 -o 新路径配置文件 命令来进行打包
    • 网页中只要引用最后打包好的文件即可