JavaScript 进阶(ES6)

JavaScript 进阶(ES6)


简介

一、ES6 基础语法

1、初识ES6

(1)ES6 简介

  • ES6 是 ECMAScript这门语言的第6代标准
  • ECMA(欧洲计算机制造商协会)是一个标准化组织;ECMAScript 是由 ECMA 这个标准化组织制定的一个语言标准
  • ES6 <=> ES2015,ES6是2015年6月发布的版本,故也可以称为ES2015
  • ECMAScript = 语法 + API
  • ES与JavaScript的关系: JavaScript = ECMAScript(语法 + API) + DOM + BOM

ES6的兼容性:

  • 主流浏览器的最新版本几乎全部支持 ES6
  • IE 老版本等不支持的浏览器,可以使用 Babel 转码

(2)let 和 const

let 和 const 是什么:

  • let 和 const 是用来声明变量或常量的关键字
  • let 代替 var 声明变量
  • const 是 constant 的缩写,用来声明常量

什么是变量,什么是常量:

  • var 、let 声明的就是变量,变量一旦初始化后,还可以重新赋值
  • const 声明的是常量,常量一旦初始化,就不能重新赋值了,否则就会报错

为什么需要 const:

  • const 是为了防止那些一旦初始化就不希望重新赋值的情况设计的

使用 const 的注意事项:

  • 使用 const 声明常量,一旦声明,就必须立即初始化,不能留到后面赋值
  • const 声明的常量,允许在不重新赋值的情况下修改它的值,例如:对象可以通过对象.属性的方式赋值

(3)let、const 与 var 的区别

  • 是否允许重复声明:已经存在的变量或常量,又声明一遍
    var 允许重复声明,let 和 const 不允许
var a = 1;
// 很多行代码之后
...
var a = 2;
console.log(a);    // 2 不会报错

let b = 1;
...
let b = 3;       
console.log(b);     // 会报错
  • 是否存在变量声明提升:先调用,后声明定义
    var 会提升变量的声明到当前作用域的顶部
    let 和 const 不存在变量声明提升
console.log(a);     // undefined
var a = 1;
console.log(a);     // 1

// 变量声明提升相当于
var a;
console.log(a);     // undefined
a = 1;
console.log(a);     // 1
console.log(a);     // 报错:变量 a 还没有初始化
let a = 1;
console.log(a);     // 1
  • 是否存在暂时性死区:只要作用域内存在 let、const,它们所声明的变量或常量就自动“绑定”这个区域,不再受到外部作用域的影响
    let、const 存在暂时性死区
    var 不存在暂时性死区
let a= 1;
function fun() {
	// 由于在 function 内部作用域中存在 let声明同名的变量
	// 此时的变量 a 就不会再向外部作用域寻找该变量 a 的值了
	// 这叫做 let 的暂时性死区(const 同理)
	console.log(a);   // 会报错
	let a = 2;
}
// 若改为如下
function fun() {
	// 由于在该作用域内没有 let、const定义的同名变量
	// 故变量a的值可以向外层作用域寻找
	console.log(a);   // 1
}
var a= 1;
function fun() {
	// var 不存在暂时性死区
	console.log(a);   // 1
	var a = 2;
}
  • 是否会变为 window 对象的属性和方法
    在全局作用域中,var 声明的变量,通过 function 声明的函数,会自动变为 window 对象的属性或方法
    let、const 不会
// var/function
var a = 12;
function fun() {
	console.log(window.a);             // 12
	console.log(window.fun === fun);   // true
}
// let/const
let a = 12;
const fun = function() {};
console.log(window.a);             // undefined
console.log(window.fun === fun);   // false
  • 是否是块级作用域
    var 没有块级作用域
    let、const 有块级作用域
// var
for(var i = 0; i < 3; i++) {
	console.log(i);         // 输出:0 1 2
}
console.log(i);             // 3

// let、const 有块级作用域
for(let i = 0; i < 3; i++) {
	console.log(i);         // 输出:0 1 2
}
console.log(i);             // 报错:变量 i 没有声明定义

作用域链:
内层作用域 -> 外层作用域 -> … -> 全局作用域

常见块级作用域:
{}、for(){}、while、do{}while()、if、switch

2、模版字符串

(1)模版字符串是什么

  • 模版字符串是使用反引号包裹的字符串(在键盘左上角)
  • 数据拼接时用“${}”绑定数据

模版字符串与普通字符串的区别:

  • 和其它东西一起使用的时候,使用模版字符串方便注入
  • 其它情况下使用模版字符串和一般字符串美多大区别
var xiaoming = {
	name: '小明',
	age: 12,
	sex: '男'
};
// 普通字符串拼接
var info = "我叫" + xiaoming.name + ",今年" + xiaoming.age + "岁了";
// 模版字符串拼接
var info = `我叫${xiaoming.name},今年${xiaoming.age}岁了`;

(2)模版字符串的注意事项

输出多行字符串:

  • 模版字符串中,所有的空格、换行或缩进都会保留在输出之中
// 一般字符串
const info1 = '第一行\n第二行';
// 模版字符串
const info2 = `第一行\n第二行`;
// 或
const info3 = `第一行
第二行`;

如何输出 ` 和 \ 等特殊字符:

  • 在想要输出的特殊字符前加反斜杠\
console.log('\"');   // 输出:"
console.log('\\');   // 输出:\

模版字符串的注入(什么东西可以放进${}):

  • 只要最终可以得出一个值的就可以通过 ${} 注入到模版字符串中

3、箭头函数

(1)箭头函数是什么

箭头函数的结构:

  • const/let 函数名 = 参数 => 函数体
  • const 函数名 = (参数)=>{函数体};
  • 由于箭头函数是匿名函数,不能直接使用,所以需要赋值给一个变量或常量

如何将一般函数改写成箭头函数:

  • 原来函数的形式有声明形式和函数表达式的形式
// 声明形式
function fun() {}
// 函数表达式形式
const fn = function() {}
  • 如果是声明形式,需要转为函数表达式的形式,再从函数表达式形式变为箭头函数形式

(2)箭头函数的注意事项

单个参数:

  • 单个参数可以省略圆括号
  • 无参数或多个参数不能省略圆括号
const fun = (a)=> {

};
// 简写为
const fun = () => {

};

单行函数体:

  • 单行函数体可以 同时省略 {} 和 return
const fun = (a,b)=> {
	return a + b;
};
// 简写为
const fun = (a,b) => a + b;

单行对象:

  • 如果箭头函数返回单行对象,可以在 {} 外面加上 (),让浏览器不再认为那是函数体的花括号
const fun = (a,b)=> {
	return {
		value:a + b;
	};
};
// 简写为
const fun = (a,b) => ({
	value: a + b
});

(3)this 指向

  • 全局作用域中的 this 指向:window
console.log(this);      // window
  • 一般函数(非箭头函数)中的 this 指向:this 指向只与调用者有关
function fun() {
	console.log(this);
}
// 严格模式下指向 undefined
// 非严格模式下指向 window
fun();
  • 箭头函数中的 this 指向:箭头函数没有自己的 this,需要从作用域链中往外寻找
const a = {
	fun: ()=>{
		console.log(this);
	}
};
// 箭头函数没有自己的this,然后向外层寻找
// 首先到对象 a,由于 对象 a 不是作用域,于是继续向外
// 然后到全局作用域,全局作用域的 this 是 window
a.fun();    // 指向 window

(4)不适合箭头函数的场景

  • 作为构造函数:构造函数最重要的就是 this,而箭头函数没有自己的 this
// 构造函数
// function People(name, age, sex) {
//     this.name = name;
//     this.age = age;
//     this.sex = sex;
// }
const Person = () => {};
new Person();
  • 需要 this 指向调用对象的时候:一般就希望this指向调用事件的对象,而箭头函数没有 this,就会向外作用域寻找,例如下面的就会指向 window
document.onclick = function() {
	console.log(this);
};
  • 需要使用 arguments 的时候:箭头函数中没有 arguments 存在

4、解构赋值

(1)什么是解构赋值

  • 解析某一数据的结构,将我们想要的东西提取出来,赋值给变量或常量
let arr = [1,2,3];
const a = arr[0];
const a = arr[1];
const a = arr[2];
console.log(a,b,c);    // 1 2 3

const [a,b,c] = [1,2,3];
console.log(a,b,c);    // 1 2 3

(2)数组解构赋值

数组的解构赋值之原理:

  • 模式(结构)匹配:赋值符号左右两边的结构相同
  • 索引值相同的完成赋值:相同结构的对应位置的变量与值一一对应
  • 不想获取的值,可以直接用逗号跳过
const [a,[,b,],c] = [1,[2,3,4],5];
console.log(a,b,c);      // 1 3 5

const [a,[,b,],c] = [1,[2,3,4],5];
console.log(a,b,c);      // 1 3 5

数组的解构赋值之默认值:

  • 默认值的基本用法:在左边的变量上直接赋值
// const [a,b] = [];
// 等价于
// const [a,b] = [undefined,undefined];
const [a = 1,b = 2] = [];
  • 默认值的生效条件:只有一个数组成员严格等于(===)undefined时,对应的默认值才会生效
const [a = 1,b = 2] = [];
console.log(a,b);           // 1 2
const [a = 1,b = 2] = [3,0];
console.log(a,b);           // 3 0
const [a = 1,b = 2] = [3,null];
console.log(a,b);           // 3 null
const [a = 1,b = 2] = [3];
console.log(a,b);           // 3 2
  • 默认值表达式:如果默认值是表达式,则默认值表达式是惰性求值的,即如果用不到表达式,表达式则不会执行
const fun = () => {
	console.log('执行了');
	return 2;
};
const [x = fun()] = [1];
// 由于 1 不是undefined,所以不会使用默认值
// 同时因为默认值表达式是惰性的,故 fun() 不会被执行
console.log(x);           // 1

const [x = fun()] = [];
// 由于右边是 undefined,所以会使用默认值
// 所以 fun() 会被执行
console.log(x);           // 2

数组的解构赋值之应用:

  • 常见的类数组的解构赋值:arguments 和 NodeList
// arguments 是类数组,不是真正的数组,所以没有数组的常用方法
// function fun() {
// 	 console.log(arguments);   
// };
// fun(1,3);          // 1 3
// fun(1,2,3,4,5);    // 1 2 3 4 5
// 传几个参数就需要对应的结构解构
function fun() {
	// const [a,b] = arguments;
	// console.log(a,b);
	const [a,b,c] = arguments;
	console.log(a,b,c);   
};
// fun(1,3);          // 1 3
fun(1,2,3);           // 1 2 3

// NodeList
<p>12</p>
<p>123</p>
<p>12345</p>
const [p1,p2,p3] = document.querySelectorAll('p');
console.log(p1,p2,p3);
  • 函数参数的解构赋值
const arr = [1,2,3];
// 原来的方式
const add = arr => {
    console.log(arr[0] + arr[1] + arr[2]);    // 6
};
// 解构的方式
const add = ([a,b,c]) => {
	console.log(a + b + c);    // 6
};
  • 交换变量的值
const a = 1;
const b = 2;
// 原来的方式
// const temp = a;
// a = b;
// b = temp;
// console.log(a,b);      // 2 1
// 解构的方式
const [a,b] = [b,a];
// 相当于 const [x,y] = [b,a] 左边的变量a、b和右边的a、b没有关系
console.log(a,b);      // 2 1

(3)对象解构赋值

对象的解构赋值之原理:

  • 模式(结构)匹配:赋值符号左右两边的结构相同
{} = {}
  • 属性名相同 的完成赋值:只要属性名相同就行,顺序可以随便
  • 不想获取的值,可以不写,但不能用逗号跳过
  • 简写也可以去掉引号(对于合法标识符来说),或者直接将右边的属性名作为左边的属性名
// 正常写
// 左边的值可以取别名,如下:age1
const {'sex':sex,'age':age1} = {username:'zhangsan', age:18, sex:'male'};
console.log(sex,age1);     // male 18
// 简写版
// 可以去掉引号
// 也可以只写键名
const {username:username,sex,age} = {username:'zhangsan', age:18, sex:'male'};
console.log(username,sex,age);     // zhangsan male 18

对象的解构赋值之默认值:

  • 默认值生效条件:对象的属性值严格等于(===)undefined 时,对应的默认值才会生效;默认值的赋值用 = 号
const {username = 'zll', age = 0} = {username: 'zhangsan'};
console.log(username,age);   // zhangsan 0
  • 默认值表达式:如果默认值是表达式,则默认值表达式的惰性求值的,即如果用不到表达式,表达式则不会执行

对象的解构赋值之注意事项:

  • 将一个已经声明的变量用于解构赋值:整个赋值需要在圆括号中进行(“{}”不同于数组的“[]”,它会被浏览器当做代码块)
let x = 2;
({x} = {x: 3});
console.log(x);
  • 可以取到继承的属性
const {toString} = {};
console.log(toString);     // function

对象的解构赋值之应用:

  • 函数参数的解构赋值
// 原来的做法
const personInfo = user => {
	console.log(user.username,user.age);
};
personInfo({username:'lgk',age: 12});
// 解构赋值
// const personInfo = ({age,username}) => {
const personInfo = ({age = 0,username = 'zll'}) => {
	console.log(username,age);
};
personInfo({username:'lgk',age: 12});
  • 复杂的嵌套
const obj = {
	a: 1,
	b: [2,3,4],
	c: {
		x: 11,
		y: 12
	}
};
// 如果想获得 3
const {b:[,m,]} = obj;
console.log(m);        // 3
// 如果想获得 4 12
const {b:[,,b3],c: {y}} = obj;
console.log(b3,y);    // 4 12

(4)其它数据类型解构赋值

  • 解构赋值只有两种形式,要么是数组的形式,要么是对象的形式

其它数据类型的解构赋值之字符串:

  • 字符串既可以用数组形式解构赋值
const str = 'hello';
const [a,b,,c,d] = str;
console.log(a,b,c,d);    // h e l o
  • 也可以用对象的形式解构赋值:索引位置作为属性名
const str = 'hello';
const {0:a,1:b} = str;
console.log(a,b);    // h e

其它数据类型的解构赋值之数值和布尔值: 先将 = 右边的值转为对象,然后再解构赋值,然后就可以取到继承的属性

const {a = 1,toString} = 123;
console.log(a,toString);  // 1 function

const {b = 2,toString} = true;
console.log(b,toString);  // 2 function

其它数据类型的解构赋值之undefined 和 null: 会尝试先将 = 右边的值转为对象,但由于 undefined 和 null 无法转为对像,所以对它们进行解构赋值,都会报错(无论是采用数组还是对象的形式都会报错)

5、对象字面量的增强与函数参数默认值

(1)对象字面量的增强

对象字面量是什么:

  • 实例化构造函数生成对象
const person = new Object();
person.name = 'zhangsan';
person.age = 12;
person.sayHello = function() {};
  • 对象字面量生成对象
const person = {
	name: 'zhangsan',
	age: 12,
	sayHello: function() {};
};

属性和方法的增强(简洁表示法):

  • 属性的简洁表示法:当键名和变量或常量名一样时,可以只写一个(对于合法的标识符)
const age = 12;
const person = {
	// 'age': age,
	// age: age,
	// 以上两种可以简写为
	age,
};
  • 方法的简洁表示法:可以省略冒号“:”和 function 关键字
const person = {
	// sayHello: function() {},
	// 可以简写为
	sayHello() {},
};

(2)方括号语法

方括号语法的用法: 方括号语法可以写在对象字面量中

const prop = 'age';
const person = {};
// 原来想将 age 作为 person 的属性
person[prop] = 12;
// ES6增强后,现在想将 age 作为 person 的属性
const person = {
	[prop]: 12,
};

方括号中可以放什么: 类似于 ${},值或通过计算(表达式)得到值的都可以放

方括号语法和点语法的区别: 点语法是方括号语法的特殊形式

  • 属性名是合法标识符的时候可以使用点语法
  • 其它情况下使用方括号语法
const person = {};
person.age 等价于 person['age']

(3)函数参数的默认值

函数参数默认值是什么: 调用函数的时候传参,就使用传递的参数,如果没传参,就用默认值

函数参数默认值的基本用法: 类似于解构赋值默认值,可以直接在行参上赋默认值

const add = (a = 0, b = 0) => {
	return a + b;
};
console.log(add(1));      // 1
console.log(add(2,3));    // 5

函数参数默认值的注意事项:

  • 默认值的生效条件:类似于解构赋值,当不传参数或明确传递的参数为 undefined 时,默认值才会生效
  • 默认值表达式:如果默认值是表达式,默认值表达式是惰性求值的,即如果用不到表达式,表达式则不会执行
  • 设置默认值的小技巧:函数参数的默认值,最好从参数列表的右边开始设置,否则左边不传的的实参必须写 undefined,不写会报错
const add = (a = 0, b) => {
	return a + b;
};
console.log(add(undefined,1));      // 1
console.log(add(,3));               // 报错

函数参数默认值的应用: 使用如下方式可以解决的问题

const hello = ({username = 'zhangsan', age = 0, sex = 'male'} = {}) => {
	console.log(username,age,sex);
};
hello();
  • 接收很多参数的时候:需要对应位置传参,参数很多时不易找对应位置
const hello = (username = 'zhangsan', age = 0, sex = 'male') => {
	console.log(username,age,sex);
};
hello(‘xiaoming’,12,'male');
  • 接收一个对象作为参数:传参麻烦,调用也麻
const hello = options => {
	console.log(options.username,options.age,options.sex);
};
hello({username: 'zhangsan', age: 0, sex: 'male'});
  • 直接使用解构赋值:当什么都不传就相当于传一个 undefined ,就会报错
const hello = ({username = 'zhangsan', age = 0, sex = 'male'}) => {
	console.log(username,age,sex);
};
hello({username: 'lgk'});

小结

  • 模版字符串:使用 反引号 后可以在里面使用 ${} 形式绑定数据
  • 箭头函数箭头函数没有自己的 this,需要从作用域链中往外寻找
  • 解构赋值:只有数组形式和对象形式的解构赋值;只有当值严格等于(===)undefined 时才会使用默认值;默认值表达式是惰性求值的
  • 数组解构:不想获取的值,可以直接用逗号跳过
  • 对象解构:只要属性名相同就行;不想获取的值,可以不写,但不能用逗号跳过;已经声明的变量用于解构赋值,整个赋值需要在圆括号中进行
  • 字符串解构赋值:字符串解构赋值方式有数组形式和对象形式
  • 方括号语法和点语法的区别: 点语法是方括号语法的特殊形式;属性名是合法标识符的时候可以使用点语法;其它情况下使用方括号语法
  • 函数参数默认值: 要使用既有对象解构赋值,又有解构赋值默认值,还有函数参数默认值的方式,尤其注意 = {} 这个函数参数默认值不能少
const hello = ({username = 'zhangsan', age = 0, sex = 'male'} = {}) => {
	console.log(username,age,sex);
};

二、ES6 扩展语法

1、剩余参数与展开运算符

(1)剩余参数

什么是剩余参数:

  • 剩余参数用“…合法标识符”表示,一般使用“…args”表示
  • 剩余参数在使用时只要写 …后面的参数名 即可
  • 剩余参数永远是一个数组,即使没有值也是空数组
  • 剩余参数区别于固定行参,只有当所传入的实参个数超过固定行参个数,多出的实参放在剩余参数数组中
const fun = (a,b,...args) => {
	console.log(a,b,args);
};

剩余参数的注意事项:

  • 箭头函数的剩余参数:箭头函数的参数部分即使只有一个剩余参数,也不能省略圆括号
const fun = (...args) => {};
  • 使用剩余参数替代 arguments 获取实际参数:箭头函数没有 arguments,剩余参数是一个数组,比 arguments 这个类数组更好用
const fun = (...args) => {
	console.log(args);
};
  • 剩余参数的位置:剩余参数只能是最后一个参数,否则会报错

剩余参数的应用:

  • 可以当做一个数组使用
  • 剩余参数不一定非要作为函数参数使用,也可以配合解构赋值使用
const [a,...args] = [1,2,3,4,5];
console.log(a,args);  // 1 [2,3,4,5]
  • 剩余参数配合解构赋值赋值使用时,也可以当做对象
const {a, ...args} = {b: 1, a: 2, x: 3, y: 5};
console.log(a,args);  // 2 {b: 1, x: 3, y: 5}

(2)展开运算符

认识展开运算符:

  • 由于一些方法不能直接接收数组作为参数,所以可以使用展开运算符将数组展开为参数列表形式
  • 展开运算符和剩余参数很像,也是在前面加 “…”
const arr = [1,2,3];
console.log(Math.max(arr));  // 报错
console.log(Math.max(...arr));  // 3

剩余参数与展开运算符的区别:

  • 剩余参数是将参数列表转为数组
  • 展开运算符是将数组展开为参数列表
  • 转换方向不同而已

数组展开运算符的应用:

  • 复制数组
const arr = [1,2,3];
const arrCopy = [...arr];
console.log(arrCopy);    // [1,2,3]
arr.push(12);
console.log(arr);        // [1,2,3,12]
console.log(arrCopy);    // [1,2,3]
  • 合并数组
const a = [1,2,3];
const b = [5,4,5];
const c = [12,22,33];
const arr = [...a,...b,...c];
console.log(arr);    // [1, 2, 3, 5, 4, 5, 12, 22, 33]
  • 字符串转数组
const str = "hello";
const arr = [...str];
console.log(arr);  // ["h", "e", "l", "l", "o"]
  • 常见类数组转为数组:arguments 和 NodeList
function fun() {
	console.log([...arguments]);
}
fun(1,2,3);   // [1,2,3]

<p>1</p>
<p>2</p>
<p>3</p>
console.log([...document.querySelectorAll('p')]);  // [p,p,p]

对象展开运算符的应用:

  • 展开对象:对象不能直接展开,必须在 {} 中展开,类似于数组在 [] 中展开
const person = {
	name: 'zhangsan',
	age: 12,
	sex: 'male'
};
const newPerson = {...person};
console.log(person === newPerson);  // false
  • 合并对象:新对象拥有全部属性,相同属性,后者会覆盖前者
const person = {
	name: 'zhangsan',
	age: 12,
	sex: 'male'
};
const p = {
	name: 'lisi',
	height: 180,
	weight: 65
};
const newPerson = {...person, ...p}; 
console.log(newPerson);
//newPerson = {
//	name: 'lisi',
//	age: 12,
//	sex: 'male',
//	height: 180,
//	weight: 65
//};

对象展开运算符的注意事项:

  • 空对象的展开
console.log({...{}});   // {}
  • 非对象的展开:对于数字、布尔值、undefined、null的展开只能得到一个空对象;字符串的展开得到一个类对象;数组在对象中展开也是得到一个类对象
console.log({...1});   // {}
console.log({...true});   // {}
console.log({...undefined});   // {}
console.log({...null});   // {}

console.log({..."hello"});   // {0:'h', 1:'e', 2:'l', 3:'l', 4:'o'}
console.log({...['h','e','l','l','o']});   // {0:'h', 1:'e', 2:'l', 3:'l', 4:'o'}
  • 对象中对像属性的展开:不会展开对象中的对象属性

2、Set 和 Map 数据结构

(1)Set 数据结构

Set 是什么:

  • Set 是一系列无序、不可重复值的数据集合。(数组是一系列有序可重复的数据结合)
  • Set 没有下标去标示每一个值,所以 Set 是无序的,也不能像数组那样通过下标去访问 Set 的成员
  • Set 中不能有重复的成员
  • Set 只能通过 new Set(); 去实例化
// 数组的两种创建方式
const arr_1 = [1,2,3];
const arr_2 = new Array('1','2','3');
// Set 的创建
const set = new Set();

Set 实例的方法和属性:

  • Set 只有一个 size 属性:用于查看 Set 的大小
const set = new Set();
set.add(1);
set.add(2).add(3).add(4);
console.log(set.size);     // 4
  • add() 方法:用于向 Set 中添加成员;一次只能添加一个成员;add() 方法可以连着写
const set = new Set();
set.add(1);
set.add(2).add(3).add(4);
console.log(set);     // {1, 2, 3, 4}
  • has() 方法:用于判断 Set 中是否有某个成员
const set = new Set();
set.add(1);
set.add(2).add(3).add(4);
console.log(set.has(3));     // true
  • delete() 方法:删除 Set 中的成员;一次删除一个;如果要删除的成员不存在,什么都不会发生
  • clear() 方法:清空 Set 中的成员
const set = new Set();
set.add(1);
set.add(2).add(3).add(4);
set.delete(3);
console.log(set);     // {1, 2, 4}
set.clear();
console.log(set);     // {}
  • forEach() 方法:遍历 Set 中的成员;第一个参数是一个回调函数,第二个参数是指定回调函数的 this 指向;遍历是按照成员添加进 Set 的顺序遍历
  • 回调函数中有3个参数(参数名随便取):其中 value 全等于 key,s 全等于 set
const set = new Set();
set.add(1);
set.add(2).add(3).add(4);
set.forEach(function(value,key,s) {
    console.log(value,key,s);      // 
    console.log(value === key);    // true
    console.log(set === s);        // true
});

// 指定第二个参数
set.forEach(function(v) {
//    console.log(v);
    console.log(this);  // document
},document);
// 改为箭头函数时,this指向会随作用域向外层查找
set.forEach((value) => {
//    console.log(value);
    console.log(this);  // 此题的this为:window
},document);

Set 构造函数的参数: 通过在实例化 Set 时添加参数,可以更便捷的添加成员

  • 数组
const set = new Set([1,2,3,4,1,2]);
console.log(set);       // {1, 2, 3, 4}
  • 字符串、arguments、NodeList、Set实例 等
// 字符串
const set = new Set("hello");
console.log(set);       // {"h", "e", "l", "o"}
// Set 实例: set === s 是 false
const s = new Set(set);
console.log(s);
// arguments
function fun() {
	console.log(new Set(arguments));
}
fun(1,2,3);   // {1,2,3}
// NodeList
<p></p>
<p></p>
console.log(new Set(document.querySelectorAll('p'))); // {p,p}

Set 的应用:

  • Set 是如何判断重复的:基本遵循严格相等(===),NaN 是特殊,Set中 NaN 等于 NaN
const set = new Set([1,2,1]);
console.log(1 === 1);  // true
console.log(set);      // {1,2}

const set2 = new Set([1,NaN,2,1,NaN]);
console.log(NaN === NaN);  // false
console.log(set2);      // {1, NaN, 2}

const set3 = new Set();
set3.add({}).add({});
console.log({} === {});  // false
console.log(set3);       // {{…}, {…}}

const set4 = new Set();
set4.add();
set4.add();
console.log(set4);       // {undefined}
  • 什么时候使用 Set:数组或字符串去重;不需要通过下标访问,只需要遍历;未来使用 Set 提供的方法和属性时(add、delete、clear、has、forEach、size 等)
// 数组去重
const set = new Set([1,2,6,3,2,1,4,5,4,2]);
const arr1 = [];
set.forEach(function(v) {
    arr1.push(v);
});
console.log(arr1);       // [1, 2, 6, 3, 4, 5]
const arr2 = [...set];
console.log(arr2);       // [1, 2, 6, 3, 4, 5]
// 字符串去重
const set = new Set("helloworldabcdefg");
const arr = [...set];
const str = arr.join('');
console.log(str);        // helowrdabcfg

(2)Map 数据结构

Map 是什么:

  • 键值对的集合,和对象很相似
  • Map 的创建只能通过 new Map();
// 对象
const person = {
	name: 'lgk',
	age: 12
};
// Map 的创建
const map = new Map();

Map 和对象的区别:

  • 对象一般用字符串当做键,使用其它类型也会转为字符串
  • Map 中 基本数据类型、引用数据类型 都可以作为 Map 的键,不会转为字符串

Map 实例的方法和属性:

  • set() 方法:用于向 Map 添加新成员,如果键已经存在,后添加的键值对覆盖已有的
const map = new Map();
map.set('name','zhansan');
map.set('age',12).set('sex','male');
console.log(map);     // {"name" => "zhansan", "age" => 12, "sex" => "male"}
  • get() 方法:用于获取 Map 中的成员;如果键不存在,则返回 undefined
const map = new Map();
map.set('name','zhangsan');
map.set('age',12).set('sex','male');
console.log(map.get('name'));      // zhangsan
console.log(map.get('age'));       // 12
console.log(map.get('hello'));     // undefined
  • has() 方法:用于判断 Map 中有没有指定的键
const map = new Map();
map.set('name','zhangsan');
map.set('age',12).set('sex','male');
console.log(map.has('name'));      // true
console.log(map.has('hello'));     // false
  • delete() 方法:删除指定的键值对;如果删除不存在的键,什么都不会发生
const map = new Map();
map.set('name','zhangsan');
map.set('age',12).set('sex','male');
map.delete('sex');
console.log(map);      // {"name" => "zhangsan", "age" => 12}
map.delete('hello');
console.log(map);     
  • clear() 方法:清空 Map 集合
const map = new Map();
map.set('name','zhangsan');
map.set('age',12).set('sex','male');
map.clear();
console.log(map);      // {}
  • forEach() 方法:遍历 Map;和 Set 类似,有两个参数,第一个是回调函数,第二个是指定 this
const map = new Map();
map.set('name','zhangsan');
map.set('age',12).set('sex','male');
map.forEach(function(value,key,map) {
	console.log(value,key,map === map);
	// console.log(this);
},document);
  • size 属性:查看 Map 中有几个成员
const map = new Map();
map.set('name','zhangsan');
map.set('age',12).set('sex','male');
console.log(map.size);   // 3

Map 构造函数的参数:

  • 数组:只能传二维数组
const map = new Map([
	['name','zhangsan'],
	['age',12],
	['sex','男']
]);
console.log(map);  // {"name" => "zhangsan", "age" => 12, "sex" => "男"}
  • Set、Map 实例对像等
// Set
const set = new Set([
	['name','zhangsan'],
	['age',12],
	['sex','男']
]);
const map = new Map(set);
console.log(map);  // {"name" => "zhangsan", "age" => 12, "sex" => "男"}

// Map
const m = new Map(map);
console.log(m); // {"name" => "zhangsan", "age" => 12, "sex" => "男"}

Map 的注意事项:

  • Map 是如何判断键名相同:基本遵循严格相等(===),但 NaN 例外
const map = new Map();
map.set(NaN,1);
map.set(NaN,2);
console.log(map);      // {NaN => 2}
console.log(map.size); // 1

什么时候使用 Map:

  • 如果只是需要 k - v 结构,或者需要使用字符串以外的值作为键,使用 Map
  • 只有模拟现实的实体时,才使用对像

3、遍历器(Iterator)与 for … of 循环

(1)遍历器(Iterator)

Iterator 是什么:

  • Iterator 称为遍历器,又称为迭代器
  • Iterator 的作用是用来遍历的

寻找 Iterator:

  • 数组等可遍历的原型链(__proto__)上存在 Symbol.iterator 方法
  • Symbol.iterator 称为可遍历对像的生成方法
  • 它的方法下存在一个 next() 方法
  • 调用 next() 方法后会有 {value: 值, done: 当前值是否不存在}

如何使用 Iterator:

  • 得到的 it 称为可遍历对像(或可迭代对像)
const it = [1,2][Symbol.iterator]();
console.log(it.next());    // {value: 1, done: false}
console.log(it.next());    // {value: 2, done: false}
console.log(it.next());    // {value: undefined, done: true}

为什么需要 Iterator:

  • 遍历数组有 for循环和forEach 方法;遍历对像有 for … in 循环
  • Iterator 遍历器是一个统一的遍历方式
  • 由于使用 Iterator 太过麻烦,一般不使用它,而是使用封装好的 for … of 循环
  • Iterator 是 for … of 的底层实现

使用了 Iterator 的场合:

  • 数组的展开运算符
  • 数组的解构赋值
  • Set 和 Map 的构造函数

(2)for … of 循环

  • for … of 循环只会遍历那些 done 为 false 时,对应的 value 值
  • for … of 循环如何遍历值或索引
  • 正常使用 for of 遍历的是值
  • keys() 得到的是索引的可遍历对像,可以遍历出索引值
  • values() 得到的是值的可遍历对像,可以遍历出值
  • entries() 得到的是 索引+值 组成的数组的可遍历对像;也可以解构得到单独的 索引 值
const arr = [1,2,3];
// 正常使用 for of 遍历的是值
for(const item of arr) {
	console.log(item);     // 1 2 3
}
// keys() 得到的是索引的可遍历对像,可以遍历出索引值
for(const key of arr.keys()) {
	console.log(key);     // 0 1 2
}
// values() 得到的是值的可遍历对像,可以遍历出值
for(const value of arr.values()) {
	console.log(value);     // 1 2 3
}
// entries() 得到的是 索引+值 组成的数组的可遍历对像
for(const entrie of arr.entries()) {
	console.log(entrie);     // [0, 1] [1, 2] [2, 3]
}
// 也可以解构得到单独的 索引 值
for(const [idx,value] of arr.entries()) {
	console.log(idx,value);     // 0 1 
								// 1 2 
								// 2 3
}

(3)原生可遍历和非原生可遍历

什么是可遍历:

  • 只要有 Symbol.iterator 方法,并且这个方法可以生成可遍历对像,就是可遍历的
  • 只要是可遍历,就可以使用 for … of 循环来统一遍历

原生可遍历:

  • 数组、字符串、Set、Map、arguments、NodeList都是原生可遍历的

非原生可遍历:

  • 一般的对象

4、ES6 的新增方法

(1)字符串的新增方法

includes() 方法: 判断字符串中是否包含某些字符

const str = "ascsdbbbsmbka";
// 基本用法
console.log(str.includes('s'));     // true
console.log(str.includes('as'));    // true
console.log(str.includes('bsm'));   // true
console.log(str.includes('ass'));   // false
// 第二个参数:表示开始搜索的位置,默认是 0
console.log(str.includes('s'));       // true
console.log(str.includes('as',0));    // true
console.log(str.includes('bsm',1));   // true

padStart() 和 padEnd() 方法:
trimStart() 和 trimEnd() 方法:

(2)数组的新增方法

includes() 方法:
Array.from() 方法:
find() 和 findIndex()方法:

(3)对像的新增方法

Object.assign() 方法:
Object.keys()、Object.values()、Object.entries()方法:

小结

三、Promise 与 Class类

1、Promise

(1)初识 Promise

  • Promise 是什么:Promise 是异步操作的一种解决方案;一般用来解决层层嵌套的回调函数(回调地狱,callback hell)的问题
  • Promise 解决的不是回调函数,而是回调地狱

(2)Promise 的基本用法

实例化构造函数生成实例对象:

  • new Promise(); 用于生成实例对象
  • Promise 有一个回调函数作为参数
const p = new Promise(function() {});
const p = new Promise(() => {});

Promise 有3种状态:

  • 一开始是 pending(未完成)
  • 执行 resolve 后变成 fulfilled(也称为 resolved),表示已成功
  • 执行 reject 后变成 rejected,表示已失败
  • Promise 的状态一旦变化,就不会再改变了
const p = new Promise((resolve, reject) => {
	// 调用 resolve() 方法
	resolve();
	// 调用 reject() 方法
	rehect();
}).then(() => {}, () => {});

(3)Promise 的实例方法

then() 方法:

  • 什么时候执行:调用 resolve() 方法或 reject() 方法时执行,只不过调用 resolve 方法时执行 then 方法的第一个回调函数;调用 reject 方法时执行 then 方法的第二个回调函数
  • 执行后的返回值是什么:返回的是一个 Promise 对象。得到的这个 Promise 对象又可以调用自己的 then 方法,可以不断的调用
  • then 方法返回的 Promise 对象的状态改变:由前一个 Promise 对象决定
  • 向后传值:前一个 Promise 传什么就接收什么
new Promise((resolve, reject) => {
	// 调用 resolve() 方法,pending -> resolved 时,执行 then 的第一个回调函数
	resolve();
	// 调用 reject() 方法,pending -> rejected 时,执行 then 的第二个回调函数
	rehect();
})
.then(() => {
	console.log('success');
	// 在 then 的回调函数中,return 后面的东西,会用 Promise 包装一下
	return undefined;
	// 等价于
	return new Promise((reslove,reject)=>{
		// 默认调用 reslove() 方法
		// 返回什么就相当于将什么作为参数传给 reslove() 方法
		reslove(undefined);
	});
	// 若是想调用 reject 方法,需要手动写
	// return new Promise((reslove,reject)=>{
		// 将想要传的值作为参数传给 reject() 方法即可
	// 	reject(undefined);
	// });
}, () => {})
.then(
(data)=>{
	// 默认返回的永远都是成功状态的 Promise 对象
	console.log(data);   // undefined
},()=>{})
// ....then(()=>{},()=>{});

catch() 方法:

  • catch() 方法是 then 方法的一个特例,专门用来处理 rejected 状态,即 调用 reject() 后执行失败的回调函数
  • catch() 等价于 then(null, () => {})
  • catch() 返回的也是一个 Promise 对象,后面依然可以继续跟 then 方法
  • catch() 可以捕获前面的错误,只要前面没有捕获过,就会一直传递下去,直到捕获;若是前一个 catch() 已经捕获错误,后面的 catch() 就只能捕获前一个catch() 之后的错误
  • 建议 Promise 对象后面要跟 catch 方法,这样可以处理Promise 内部发生的错误
new Promise((reslove, reject) => {
	//reslove();
	reject();
}).then(data => {

})
// .then(null, err => {
// 
// })
// 注释部分等价于下面
.catch(err => {

});

finally() 方法:

  • 当 Promise 状态发生变化时,不论如何变化都会执行 finally() 方法,不变化不执行
  • finally() 方法也是 then 方法的一个特例
  • 虽然都会执行,但 finally() 方法接收不了参数
  • finally() 的典型应用是用于关闭数据库连接
new Promise((reslove, reject) => {
	// 调用这两个方法都会执行 finally() 方法
	// reslove();
	// reject();
}).finally(data => {

}).catch(err => {

});

(4)Promise 的构造函数方法

Promise.resolve():

  • Promise.resolve() 是成功状态 Promise() 的一种简写形式
new Promise(reslove => {
	reslove('参数');
}).then(data => {});
// 可简写为
Promise.reslove('参数').then(data => {});
  • 接受的参数:当 resolve 方法接受的参数为 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调函数
const p = new Promise(reslove => {
	console.log('参数为 Promise 对象');
});
Promise.reslove(p).then(data => {}).catch(err => {});

Promise.reject()

  • Promise.reject() 是失败状态 Promise() 的一种简写形式
  • 不管什么参数,都会原封不动的向后传,作为后续方法的参数
new Promise((null, reject) => {
	reject('参数');
}).catch(err => {});
// 可简写为
Promise.reject('参数').catch(err => {});

Promise.all()

  • Promise.all() 关注多个 Promise 对象的状态变化
  • 将传入多个 Promise 实例,包装成一个新的 Promise 实例返回
  • Promise.all() 的状态变化与所有传入的 Promise 实例对象的状态有关,只有所有状态都变成 resolved,最终的状态才会变成 resolved;只要有一个变成 rejected,最终的状态就变成 rejected
const p1 = new Promise(reslove => {
	setTimeout(reslove => {
		console.log('p1执行了');
	},1000);
});
const p2 = new Promise(reslove => {
	setTimeout(reslove => {
		console.log('p2执行了');
	},2000);
});
const p = Promise.all([p1,p2]).then(data => {
	console.log(data);
}).catch(err => {
	console.log(err);
});

Promise.race() 和 Promise.allSettled()

  • 都是用来关注 Promise 对象状态变化
  • Promise.race() 的状态取决于第一个完成的 Promise 实例对象,如果第一个完成的是成功,那最终的就是成功;如果第一个完成的失败,那最终的就是失败
  • Promise.allSettled() 的状态与传入的 Promise 状态无关,永远都是成功,它的作用更多像是一个记录员记录下各个 Promise 的表现

(5)Promise 的注意事项和应用

Promise 的注意事项:

  • resolve 或 reject 方法执行后后面的代码是否执行:还会执行,但不建议在 resolve 或 reject 方法后还有代码需要执行,应该在调用 resolve 或 reject 方法的时候加上return,原来想在后面执行的代码可以放到 then 中执行
  • Promise.all / race / allsettled 的参数问题:这些方法的参数如果不是 Promise 数组,会将不是 Promise 的数组元素转变成 Promise 对象;任何可遍历的都可以作为参数
  • Promise.all / race / allsettled 的错误处理:分别给每个对象单独 catch 处理错误;统一在 Promise.all / race / allsettled 方法调用 catch 方法处理

2、Class 类

(1)初识 Class

Class 是什么: 类可以看做是对象的模版,用一个类可以创建多个不同的对象

Class 的两种定义形式:

  • 声明形式 和 表达式形式
  • 类名一般首字母大写;类名后没有圆括号;大括号后面没有分号“;”
  • 实例化执行构造方法,所以必须有构造方法,但可以不写
  • 构造函数里的 this 就是实例化后的对象
  • 一般在构造函数中定义属性,方法不在构造方法中定义(原因是不共享,每次新创建实例时也会创建新的方法,资源浪费)
  • 方法直接定义在类中,各实例共享方法
  • 方法之间没有逗号
// 声明形式
class Person {
	constructor(name, age) {
		this.name = name;
		this.age = age;
	}
	// 各实例共享的方法
	hello() {}
}
// 实例化
const p = new Person();

// 表达式形式
const Person = class {
	constructor() {}
};

// 立即执行函数
(function Person() {})();
// 立即执行的类
new (class {
	constructor() {}
})();

Class 与构造函数的区别:

// Class
class Person {
	constructor() {}
	method_() {}
}
// 构造函数
function Person() {}
Person.prototype.method_ = function() {};

(2)Class 的属性和方法

实例属性、静态方法和静态属性:
私有属性和方法:

(3)Class 的继承

extends:
super:

(4)Class 的应用

四、Module模块 与 Babel编译

1、Module 模块

(1)初识 Module

什么是 Module:

  • 一个一个的局部作用域的代码块

什么是 Module 系统:用于解决如下问题

  • 模块化问题
  • 消除全局变量
  • 管理加载顺序

Module 的基本用法:

  • 使用 module 前需要搭建服务器系统,在 VSCode 插件中心安装 Live Server
  • 只要会用到 import 或 export,在使用 script 标签加载的时候,就要加上 type="module"

(2)Module 的导入和导出

导出和导入:

  • 导出的东西可以被导入(import),并访问到
  • 一个模块即使没有导出,也可以将其导入;被导入的代码都会执行一遍,也仅会执行一遍(无论导入多少次)

export default 和 对应的 import:

  • 导入时的别名可以随便取(关键字和保留字除外),但为了语义化,建议同名
  • 一个模块只能有一个 export default
// module.js 文件
const age = 12;
export default age;

// test.html 文件
<script type="module">
	import age from './module.js';
	console.log(age);     // 12
</script>

export 和 对应的 import:

  • export 后面不能直接跟值,必须是声明或语句
  • 导入时的名称不能随便取,必须与导出时的名称一致,且用花括号包裹名称
// module.js 文件
//export const age = 12; // export 的写法
// 或
const age = 12; 
export {age};

// test.html 文件
<script type="module">
	import {age} from './module.js';
	console.log(age); 
</script>
  • 多个导出:可以写一个导出一个;也可以在最后时一次导出
  • 导出导入时取别名用:as
  • 整体导入:import * as obj from ‘./module.js’;
  • 同时导入:当导出同时有 export default 和 export 时,可以同时导入,但是,一定是 export default 的在前,两者之间用逗号分隔

(3)Module 的注意事项和应用

Module 的注意事项:

  • 模块顶层(该模块最外层作用域)的 this 指向:模块中,顶层的 this 指向 undefined;可以利用这种特性检查是否是以模块的方式加载
  • ./ 表示当前目录,…/ 表示父级目录
  • import 关键字和 import() 函数:import 命令具有提升效果,会提升到整个模块的头部,率先执行;import() 可以按条件导入
  • import 和 export 命令只能在模块的顶层,不能在代码块中执行
  • 导入导出的复合写法:复合写法导出的东西在当前模块中不能使用
// 导入导出复合写法
export {age} from './module.js';
// 等价于
import {age} from './module.js';
export {age} from './module.js';

2、Babel 与 Webpack

(1)Babel

Babel 是什么:

  • Babel 的中文官网:https://www.babeljs.cn
  • Babel 的在线编译:https://www.babeljs.cn/repl
  • Babel 是 JavaScript 的编译器,用来将 ES6 及之后的代码,转换成 ES6 之前的代码,以便运行在旧版的浏览器中
  • Babel 本身可以编译 ES6 的大部分语法,比如 let、const、箭头函数、类;但是对于 ES6 新增的 API,如:Set、Map、Promise 等全局对象,以及一些定义在全局对象上的方法都不能直接编译,需要借助其他的模块
  • Babel 一般需要配合 Webpack 来编译模块语法

使用 Babel 前的准备工作:

  • 什么是 Node.js 和 npm
    Node.js 是个平台或者工具,对应浏览器
    后端的 JavaScript = ECMAScript + IO + File + … 等服务端的操作
    npm 是 node 的包管理工具
  • 安装 Node.js
    Node.js 的官网:https://nodejs.org/en/
  • 初始化项目:npm init 生成 package.json 文件(如果已经有 package.json 文件就不需要 init 了)
npm init
  • 安装 Babel 需要的包:
// 切换安装源:
npm config registry https://registry.npm.taobao.org
// 安装最新版:
npm install --save-dev @babel/core @babel/cli
// 安装指定版本:
// npm install --save-dev @babel/core@7.11.0 @babel/cli@7.10.5

使用 Babel 编译 ES6 代码:

  • Babel 的配置文件
    告诉 Babel 将代码转换成什么版本:
npm install @babel/preset-env --save-dev
// npm install @babel/preset-env@7.11.0 --save-dev

需要在项目根目录下创建:babel.config.json(原来是:.babelrc) 文件,然后在文件中添加如下语句

{
    "presets": ["@babel/preset-env"]
}
  • 编译的命令:
    在 package.json 文件中添加如下
// babel src -d lib 意思是将源目录输出到 lib 目录下,-d 表示输出
// 原写法:babel src --out-dir lib
// src 和 lib 文件目录名可以改,默认使用 src lib
"scripts": {
	"build": "babel src -d lib"
},

然后就可以编译项目:

npm run build

(2)Webpack 基础入门

Webpack 是什么:

  • Webpack 的官网:https://www.webpackjs.com/
  • webpack 是静态模块打包器,当 webpack c处理应用程序时,会将所有这些模块打包成一个或多个文件
  • 模块:webpack 可以处理 js/css/图片、图标字体等单位
  • 静态:开发过程中存在于本地的 js/css/图片/图片字体等文件,就是静态
  • webpack 没办法处理动态的内容,只能处理静态的

Webpack 初体验:

  • 初始化项目
npm init
  • 安装 Webpack 需要的包
// 自动安装最新
npm install --save-dev webpack-cli webpack
// 指定版本
// npm install --save-dev webpack-cli@3.3.12 webpack@4.44.1
  • 配置 Webpack:
    package.json 文件 中添加如下
"scripts": {
	// "webpack": "webpack"
	// 等价于如下
    "webpack": "webpack --config webpack.config.js"
},

在项目根目录下创建 webpack.config.js 文件

// 文件中的内容如下

// 根路径
const path = require('path');

module.exports = {
  // 这行代码的意思是:以不压缩的形式展示,默认没有
  // mode: 'development', 
  // 这是入口文件
  entry: './src/index.js',
  // 出口文件
  output: {
      // 出口文件目录的路径
      path: path.resolve(__dirname, 'dist'),
      // 输出的文件名
      filename: 'bundle.js'
  }
};
  • 打包并测试
<script src="./dist/bundle.js"></script>

(3)Webpack 的核心概念

entry 和 output:

  • entry 是指定入口文件
  • output 指定出口文件
    path 一定是绝对路径,所以 require(‘path’); 不能更改
    __dirname:目录名,指代当前文件所在的项目目录
    dist:编译项目后编译文件所在目录(名字可以随便定义)
    filename:指定输出文件名(名字可以随便定义)
// webpack.config.js
const path = require('path');

module.exports = {
    // 不希望打包后的代码压缩,使用 development,不写则默认使用 production
    mode: 'development',
    // 指定单入口
    entry: './src/index.js',
    // 上面的等价于下面这个
    entry: {
		main: './src/index.js'
    },
    // 多入口
    entry: {
		main: './src/index.js',
		search: './src/search.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        // 单入口
        filename: 'bundle.js'
        // 多入口时
        // 此处的 name 与多入口时的 k 对应,多入口时会自动生成对应的文件
        filename: '[name].js'
    }
};

loader:

  • loader 让 webpack 能够去处理那些非 JS 文件的模块,如:CSS/图片等
  • 使用 babel-loader 编译(配置 babel-loader 可以参考:https://www.webpackjs.com/loaders/
    安装 Babel 相关的包:
// 安装 Babel 相关的包
npm install --save-dev babel-loader @babel/core @babel/preset-env
// 指定版本
// npm install --save-dev babel-loader@8.1.0 @babel/core@7.11.0 @babel/preset-env@7.11.0

引入 core-js 编译新增 API
安装好 core-js 后必须在需要使用的源文件中导入:import “core-js/stable”;

npm install --save-dev core-js
// 指定版本
// npm install --save-dev core-js@3.6.5

添加 Babel 相关的配置文件:babel.config.json

{
    "presets": ["@babel/preset-env"]
}

在 webpack.config.js 中配置 babel-loader 使用Babel

const path = require('path');

module.exports = {
    entry: {
      index: './src/index.js'
    },
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
        ]
    },
};

打包并测试:

npm run webpack

plugins:

  • 什么是 plugins(插件):loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。
  • webpack 插件官网 :https://www.webpackjs.com/plugins/
  • 插件使用示例:html-webpack-plugin
    安装插件:
npm install --save-dev html-webpack-plugin

在 webpack.config.js 中配置

const path = require('path');
// 获取插件
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'development',
    entry: {
      index: './src/index.js'
    },
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
        ]
    },
    // 配置插件
    plugins: [
        new HtmlWebpackPlugin({
            // 指定模版 
            template: "./index.html"
        })
    ]
};

多页面时 html-webpack-plugin 插件的配置
在 webpack.config.js 中配置

const path = require('path');
// 获取插件
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'development',
    entry: {
      index: './src/index.js',
      search: './src/search.js'
    },
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
        ]
    },
    // 配置插件
    plugins: [
        // 多入口:有几个入口就实例化几次,并且要指定 filename
        new HtmlWebpackPlugin({
            // 指定模版 
            template: "./index.html",
            filename: 'index.html',
            // 告诉插件这个html文件需要引入哪个 js 文件
            // 方括号里面的名字是入口的 k
            chunks: ['index'],
            // 也可以在该插件添加更多配置
            minify: {
				// 删除 index.html 中的注释
				removeComments: true,
				// 删除 index.html 中的空格
				collapseWhitespace: true,
				// 删除各种 html 标签属性值的双引号
				removeAttributeQuotes: true
			}
        }),
        new HtmlWebpackPlugin({
            // 指定模版 
            template: "./search.html",
            filename: 'search.html',
            chunks: ['search']
        })
    ]
};

(4)Webpack 的应用

处理 CSS 文件:

  • 安装 css-loader
// 一起安装
npm install --save-dev css-loader style-loader
// 分开安装
npm install --save-dev css-loader 
// style-loader 的作用是将 CSS 以 style 标签的形式引入
npm install --save-dev style-loader
  • 配置
module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
            {
            	test: /\.css$/,
            	// loader: 'css-loader'
            	// 多个loader 同时配置,
            	// 注意:webpack 使用loader时是从右向左开始使用
            	use: ['style-loader','css-loader']
			}
        ]
    },
  • 上面的 style-loader 是以 style 标签的方式引入CSS,如下是以 <link>标签的方式引入CSS
    安装 mini-css-extract-plugin
npm install --save-dev mini-css-extract-plugin

在 webpack.config.js 中配置

const path = require('path');
// 获取插件
const HtmlWebpackPlugin = require('html-webpack-plugin');

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: 'development',
    entry: {
      index: './src/index.js',
      search: './src/search.js'
    },
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
            {
            	test: /\.css$/,
            	// loader: 'css-loader'
            	// 多个loader 同时配置,
            	// 注意:webpack 使用loader时是从右向左开始使用
            	// use: ['style-loader','css-loader']
            	use: [MiniCssExtractPlugin.loader,'css-loader']
			}
        ]
    },
    // 配置插件
    plugins: [
        // 多入口:有几个入口就实例化几次,并且要指定 filename
        new HtmlWebpackPlugin({
            // 指定模版 
            template: "./index.html",
            filename: 'index.html',
            // 告诉插件这个html文件需要引入哪个 js 文件
            // 方括号里面的名字是入口的 k
            chunks: ['index'],
            // 也可以在该插件添加更多配置
            minify: {
				// 删除 index.html 中的注释
				removeComments: true,
				// 删除 index.html 中的空格
				collapseWhitespace: true,
				// 删除各种 html 标签属性值的双引号
				removeAttributeQuotes: true
			}
        }),
        new HtmlWebpackPlugin({
            // 指定模版 
            template: "./search.html",
            filename: 'search.html',
            chunks: ['search']
        }),
        new MiniCssExtractPlugin({
        	filename: 'css/[name].css'
		})
    ]
};

使用 file-loader 处理 CSS 中的图片:

使用 html-withimg-loader 处理 HTML 中的图片:

使用 file-loader 处理 JS 中的图片:

使用 url-loader 处理图片:

(5)使用 webpack-dev-server 搭建开发环境


总结

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值