ES6超详细学习入门
2021-09-07 21:26:02 4 举报
AI智能生成
ES6语言入门
作者其他创作
大纲/内容
ES6基础
ES6简介
什么是ES6?
ECMAScript6
ECMAScript是语言的标准
ECMA
欧洲计算机制造商协会
标准化组织
历史版本
ES1、2比较原始的版本
ES3
do while、switch、正则等
ES4
废弃了
ES5
forEach、map、filter、Object.create、Object.defineProperty等
ES6 <=> ES2015
具体
语法和API
ES和javascript的关系
javascript=ECMAScript+DOM+BOM
ES6兼容性
主流浏览器的最新版本几乎全部支持ES6
IE老版本不支持的可以用Babel转码
let和const
用来声明变量或常量
let代替var声明变量
变量初始化之后可变
const用来声明常量
常量初始化之后不可变,不可重新赋值
声明和初始化要一起完成,不能分开
为什么需要const
为一旦初始化就不需要改变的值来设计的
const声明的常量允许不重新复赋值的情况下改版它的值,如引用类型。基本类型的数据不能修改
let、const、var区别
重复声明:已经存在的变量或者常量又声明了一边
var允许重复声明
函数的入参也属于已经存在的变量
let、const不允许重复声明
变量提升
var会将变量的声明(仅声明)提升至当前作用域的顶部
let、const不存在变量提升
暂时性死区
只要作用域内存在let、const声明的变量或常量,就自动绑定这个区域,不再收到外部作用域的影响
在当前作用域,所要使用的变量已经存在,不会再访问该作用域以外的同名变量,并且只有在声明变量之后,才可以获取和使用该变量,否则就会报错。
window对象的属性和方法
全局作用域中,使用var声明的变量或者function就会自动变成window对象下的变量和方法,也就是全局变量和方法
let、const不会
块级作用域
var没有块级作用域
for(var i=0; i<3;i++){
}
console.log(i);
}
console.log(i);
会输出3
let、const有块级作用域
for(let i=0; i<3;i++){
}
console.log(i);
}
console.log(i);
在for循环外面打印i会报错,因为let的作用域就在for循环里面
作用域链
内层作用域->外层作用域->……->全局作用域
有哪些是块级作用域?
{}
for循环
while循环
do while
if
switch
模板字符串和箭头函数
模板字符串
``
使用反引号
通过${值}注入
简单字符串,使用``和使用'' ""没有什么区别
模板字符串,和其他内容拼接混用的时候,使用模板字符串,可以方便注入
var info2 = `我是${person.name},我今年${person.age}岁了`;
注意事项
模板字符串的换行、空格、缩进等等会保留在输出之中。(怎么写就怎么输出)
输出特殊字符(` \)
使用转义
\`
\\
模板字符串的注入
什么内容可以放在模板字符串中?
常量
变量
对象的属性
调用函数
运算操作
只要最终弄可以得到一个值的,都可以使用${}进行注入
箭头函数
箭头函数结构
()=>{}
参数 => {函数体}
const add = (x, y) => {
return x + y;
}
return x + y;
}
可以将普通函数转化为箭头函数
箭头函数的注意事项
单个参数情况
()圆括号可以省略,但是无参数、多个参数不能省略
单行函数体
单行函数体可以同时省略大括号和return
单行对象
按照单行函数体改造,需要在对象的大括号外再加一个小括号,让浏览器不再认为大括号是函数体的大括号
不适用箭头函数的场景
构造函数
需要this指向调用对象的时候
因为箭头函数没有this
需要arguments的时候
箭头函数中没有arguments
this指向问题
全局作用域中的this指向
指向window
一般函数(非箭头函数)中的this指向
规则
只有在调用函数的时候this指向才能确定,不调用的时候不知道指向是谁
this指向和在哪里调用没有关系,只和谁调用有关系
function add(){
console(this);
};
add();// 这里调用的add() ,在非严格模式下,undefined 转化为了window 也就是调用了window.add();
// 在严格模式下是undefined
console(this);
};
add();// 这里调用的add() ,在非严格模式下,undefined 转化为了window 也就是调用了window.add();
// 在严格模式下是undefined
箭头函数中的this指向
箭头函数没有自己的this,通过作用域链寻找this的指向
严格模式和非严格模式
为什么使用严格模式?
严格模式消除了JavaScript语法的一些不合理、不严谨之处,减少一些怪异行为。
消除代码运行一些不安全之处,保证代码运行的安全。
提高代码编译效率,增加运行速度。
为未来新版本的JavaScript做好铺垫。
如何使用严格模式?
1、为整个脚本开启严格模式
需要在所有语句之前放一个特定语句"use strict"
<script>
// 整个脚本都开启严格模式的语法
"use strict";
let v = "Hi! I'm a strict mode script!";
</script>
// 整个脚本都开启严格模式的语法
"use strict";
let v = "Hi! I'm a strict mode script!";
</script>
2、为函数开启严格模式
把"use strict"声明放在函数体的所有语句之前
function strict() {
// 函数级别严格模式语法
'use strict';
let sum = 1;
let result = 0
result += sum
}
// 函数级别严格模式语法
'use strict';
let sum = 1;
let result = 0
result += sum
}
严格模式和非严格模式的区别?
1、将过失错误转为异常
在非严格模式中,不声明变量表示全局变量
在严格模式中,这种会报异常
2、this指向undefined
在非严格模式中,全局作用域中的函数内部this默认指向window
在严格模式中,全局作用域中的函数内部this默认指向undefined
3、不允许变量重名
非严格模式下,允许重复变量命名
严格模式下,不允许重复变量重名
解构赋值
什么是解构赋值
顾名思义,解析某一数据的解构,将我们想要的东西提取出来,赋值给变量或常量
数组的解构赋值
原理
模式(结构)匹配
赋值左右是相同的结构,都是数组
索引值相同的完成赋值,不取的逗号跳过
例子
const [a,b,c] = [1,2,3];// a 1,b 2,c 3
const [a,,c] = [1,2,3];//a 1 c 3
const [i,[,,j],,k] = [1,[2,3,4],5,6];// 1、4、6
const [m] = [1,[3,4],5];
console.log(m);// 1
const [,n] = [1,[3,4],5];
console.log();// [3,4]
console.log(m);// 1
const [,n] = [1,[3,4],5];
console.log();// [3,4]
数组解构赋值的默认值
数组解构赋值的默认值的生效条件:
只有当一个数组成员严格===undefined的时候,默认值才会生效
只有当一个数组成员严格===undefined的时候,默认值才会生效
var [i1,j1] = [];
console.log(i1,j1);//undefined undefined
var [i2=2,j2=3] = [];
console.log(i2,j2);//2 3
console.log(i1,j1);//undefined undefined
var [i2=2,j2=3] = [];
console.log(i2,j2);//2 3
// 成员值不是undefined,默认值不会生效
var [i3=2,j3=3] = [4,5];
console.log(i3,j3);//4 5
var [i4=2,j4=3] = [4,null];
console.log(i4,j4);//4 null
// 默认值生效
var [i4=2,j4=3] = [4];
console.log(i4,j4);//4 3
var [i3=2,j3=3] = [4,5];
console.log(i3,j3);//4 5
var [i4=2,j4=3] = [4,null];
console.log(i4,j4);//4 null
// 默认值生效
var [i4=2,j4=3] = [4];
console.log(i4,j4);//4 3
默认值表达式
如果默认值是表达式,表达式是惰性求值的
// 默认值表达式,惰性求值
const fun = ()=>{
console.log('我被执行了');
return 1;
}
// 用不到默认值,这个fun函数就不会被执行
const [a2=fun()] = [333];
console.log(a2);// 333
// fun被执行的情况
const [a3=fun()] = [];
console.log(a3);// 执行fun, 1
const fun = ()=>{
console.log('我被执行了');
return 1;
}
// 用不到默认值,这个fun函数就不会被执行
const [a2=fun()] = [333];
console.log(a2);// 333
// fun被执行的情况
const [a3=fun()] = [];
console.log(a3);// 执行fun, 1
实际开发中的应用
常见类数组的结构赋值
arguments
NodeList
函数参数的解构赋值
// 使用数组的解构赋值来写
// 这里的[x,y]是实参,解构赋值之类对应数组的项
const arrArr2 = ([x,y])=>{
return x+y;
}
console.log(arrArr2(arrP));// 7
// 这里的[x,y]是实参,解构赋值之类对应数组的项
const arrArr2 = ([x,y])=>{
return x+y;
}
console.log(arrArr2(arrP));// 7
交换变量的值
let x1=1;
let y1=2;
[x1,y1] = [y1,x1];
console.log(x1,y1);
let y1=2;
[x1,y1] = [y1,x1];
console.log(x1,y1);
对象的解构赋值
原理
模式(结构)匹配
属性名相同的完成赋值(可完成取别名)
const person = {username:'张三',age:19,sex:'男'};
const {username,age,sex} = person;
console.log(username,age,sex);
// 后面一个参数是可以取别名
const {username:uname,age:itage,sex:sex1} = person;
console.log(uname,itage,sex1);
const {username,age,sex} = person;
console.log(username,age,sex);
// 后面一个参数是可以取别名
const {username:uname,age:itage,sex:sex1} = person;
console.log(uname,itage,sex1);
对象解构赋值的注意事项
对象解构赋值的默认值
默认值的生效条件
只有当属性值严格===undefined的时候,默认值才会生效
和数组类似
默认值表达式
如果默认值是表达式,表达式是惰性求值的
和数组类似
将一个已经声明的变量用于解构赋值
整个的赋值需要在圆括号中完成
let x3;
//let { x3 } = { x3: 12121212121 };//Identifier 'x3' has already been declared
({ x3 } = { x3: 12121212121 });
console.log(x3);
//let { x3 } = { x3: 12121212121 };//Identifier 'x3' has already been declared
({ x3 } = { x3: 12121212121 });
console.log(x3);
可以取到继承的属性
const {toString} = {};
console.log(toString);// toString是继承来的方法
console.log(toString);// toString是继承来的方法
对象解构赋值在实际中的应用
其他数据类型的解构赋值
字符串的解构赋值
以数组形式解构赋值
const [a,b] = 'hello';// h e
以对象形式解构赋值
const {0:aa99,1:bb99,4:oo99,length} = 'hello';
数值和布尔类型的解构赋值
只能使用对象形式的解构赋值
会先将等号右边的转化为对象再解构赋值
但是得不到自己的值
但是得不到自己的值
const {a=1,toString} = true;
undefined和null的解构赋值
由于undefined和null都不能转换为对象,所以无法解构赋值,会报错
对象字面量的增强和函数参数的默认值
对象字面量的增强
属性和方法的简洁表示法
1、键名和变量名或常量名一样的时候,可以只写一个
let name='张三';
const person = {name,age:11};
const person = {name,age:11};
2、方法可以省略冒号和function关键字
const p = {
name: '张三',
say: function () {
console.log('say');
},
// 属性是方法,可以省略冒号和function关键字
speak() {
console.log('speak');
}
}
name: '张三',
say: function () {
console.log('say');
},
// 属性是方法,可以省略冒号和function关键字
speak() {
console.log('speak');
}
}
方括号语法
方括号的用法
方括号也可以运变量传进来用在对象字面量中,将属性名可作为一个变量传进来
const prop = 'sex';
const student = {name:'ellie',age:19,[prop]:'男'};
const student = {name:'ellie',age:19,[prop]:'男'};
方括号里面可以放什么
只要可以计算求得值的都可以放在方括号中,和${}注入类似
方括号和点语法的区别
点语法是方括号语法的特殊形式
属性名是合法标识符的时候才能使用点语法,其他情况可以使用方括号语法
函数参数的默认值
函数参数的默认值什么?
调用函数的时候,穿了参数就使用所传的参数,如果没有传参数,就使用默认值
const m = (x, y = 1) => x * y;
函数参数默认值的注意事项
默认值的生效条件
不传参数或者参数为undefined,默认值才会生效
默认值是表达式的情况
如果默认值是表达式,表达式是惰性求值
小技巧
从函数参数的右边开始设置默认值
函数参数默认值的应用
当传入很多很多参数的时候,可接收一个对象作为参数
剩余参数和展开运算符
剩余参数
什么是剩余参数
当不知道传入多少参数时,使用...加一个参数名老表示剩余参数
const fun = (a,b,...args)=>{}
剩作为函数参数时,余参数永远是个数组,即使没有值也是个空数组
使用剩余参数的注意事项
箭头函数的剩余参数
箭头函数如果只有一个剩余参数,也不能省略圆括号
使用剩余参数代替arguments
因为箭头函数中没有arguments
const add = (...args)=>{}
剩余参数的位置
只能是最后一个参数,后面不能再有参数了,否则会报错
应用
结合解构赋值使用,必须是最后一个值,
解构赋值又可以作为函数参数
解构赋值又可以作为函数参数
const [num,...args] = [1,2,3,4,5,6];
数组
const {x,y,...obj} = {x:1,a:2,y:3,z:4,q:91};
对象,这里就是剩余元素,是一个对象而不是数组了
展开运算符
认识展开运算符
将数组形式转化为参数列表形式
...[数组]
展开运算符和剩余参数的区分
展示运算符:是把数组展示成参数列表,[1,2,3]> 1,2,3
剩余参数:试讲参数列列表转化为数组,1,2,3-> [1,2,3]
例子
// 参数上是剩余参数
const add = (...args)=>{
console.log(args);
// 这里是展开运算符
console.log(...args);
}
add(1,2,3);
const add = (...args)=>{
console.log(args);
// 这里是展开运算符
console.log(...args);
}
add(1,2,3);
[...[1,2,3],4]
结果就是[1,2,3,4]
数组的展开运算符
拷贝数组
var array = [1,2,3,[4]];
const newArr = [...array];
const newArr = [...array];
不适用二维数组?
合并数组 const arrAB= [...arr0,2,2,...arr1,...arr2,1,1,1,1];
var arr0 = [1,2];
var arr1 = [3];
var arr2 = [4,5];
const arrA = [...arr0,...arr1,...arr2];
字符串转化为数组
const str = 'hello';
const arrrrrrr = [...str];
const arrrrrrr = [...str];
常见的类数组转化为数组
arguments
[...arguments]
NodeList
[...nodeList]
对象的展开运算符
展开对象
对象不能直接展开,需要放在{}中展开
把属性罗列出来,放在一对大括号中,构成一个新的对象
合并对象
{...person,...student}
前后属性名相同的,后面的覆盖前面的,属性名不同的都保留
{person,student}
{person:{.....},student:{......}}
{...person,student}
{name:'zhangsan',...,student:{......}}
注意事项
空对象的展开
其实没有效果,得到的还是空对象
非对象的展开
如果展开的不是对象,则会将其先转化为对象再展开
数值、undefined、null、布尔等,展开得到的还是空对象
字符串的展开
类数组对象
{...'hello'}
{o:'h',1:'e',........,4:'o'}
在对象中展开一个数组
和在对象中展开一个字符串效果一样
不会再对象中再次展开里面的对象
实际应用
复制对象
疑问:对象中含有对象复制是否有问题?
用户参数和默认参数
const logUser = user => {
// 设置一个默认值
const defaultParam = { name: 'zhangsan', age: 18, sex: '男' };
// 将对象合并,并用传入的参数覆盖默认值
let param = { ...defaultParam, ...user };
console.log(param);
}
logUser();
logUser({name:'张三'});
logUser({name:'张三',age:20});
logUser({name:'张三',age:20,sex:'女'});
// 设置一个默认值
const defaultParam = { name: 'zhangsan', age: 18, sex: '男' };
// 将对象合并,并用传入的参数覆盖默认值
let param = { ...defaultParam, ...user };
console.log(param);
}
logUser();
logUser({name:'张三'});
logUser({name:'张三',age:20});
logUser({name:'张三',age:20,sex:'女'});
reduce
Set和Map数据结构
Set
Set是什么
Set是一系列无需的、且没有重复值的数据集合
和数组有些相似之处
只能通过new Set() 来创建,不能通过字面量创建
Set没有下标去标识每一个值,所以Set是无序,不能像数组那样通过下标获取到对应的值
Set实例的方法和属性
方法
实例.add();
添加成员,可以连缀
实例.has();
是否包含某成员
实例.delete();
删除某成员,删除不存在的成员也不会报错
实例.clear();
清空
forEach遍历set
// 遍历 set中 value和key代表的一样
se.forEach(function (value,key){
console.log(value,key);
});
se.forEach(function (value,key){
console.log(value,key);
});
按照成员添加的顺序遍历的
const obj = {};
// 第二个参数时传入的上下,但是是箭头函数没有this
se.forEach((value, key, se) => {
console.log(this);// window
}, obj);
// 第二个参数时传入的上下,但是是箭头函数没有this
se.forEach((value, key, se) => {
console.log(this);// window
}, obj);
属性
实例.size
值的个数
Set构造函数的参数
数组
const set = new Set([1, 2, 6, 6, 6, 6, 6, 7, 8]);
字符串
const set0 = new Set('hello');
console.log(set0);// h e l l o
arguments、NodeList
const set1 = new Set(arguments);
Set
const set2 = new Set([1,2,1,1,1,3]);
const set3 = new Set(set2);//不是同一个属于复制
Set的注意事项
判断重复值的方式
Set对重复值的判断基本遵循严格相等===,但对于NaN不一致,Set中NaN等于NaN
// set判断重复值
const set4 = new Set([NaN,undefined,null,NaN,undefined,null,1,2,{},{}]);
console.log(set4);// NaN,undefined,null,1,2,{},{}// {}和{}不是同一个引用
什么时候使用Set
数组或字符串去重
不需要通过下标访问,只需要遍历
为了使用Set的方法和属性时
Set在实际开发中的应用
数组去重
const arr = [1,2,1];
console.log([...new Set(arr)]);
Set可以展开
字符串去重
const s = 'abcddddddeeaaa';
console.log([...new Set(s)].join(''));
单词去重
let str = "study hard and make and progress every day study";
let arr10 = str.split(' ');
let arr11 = [...new Set(arr10)].join(' ');
Map
Map是什么
Map是键值对的集合
和对象有相似之处
Map和对象的区别
对象一般用字符串当做键,Map的键没有限定,引用类型也可以当做键
只能通过new Map(),没有字面量
Map实例的方法和属性
方法
.set();
可以连写,重复的key后面覆盖前面的
.get(key)
get不存在的键结果是undefined
.has(key)
判断key是否存在
.delete(key)
删除一个不存在的key不会报错,返回false
.clear()
清空map
.forEach
属性
.size;
Map构造函数的参数
数组
只能传二维数组,并且要体现出键值对
const map0 = new Map([
['name','zhangsan'],['age',19],['sex','男']
]);
Set
也只能是键值对形式
const set9 = new Set([['name', 'zhangsan'], ['age', 19], ['sex', '男']]);
console.log(set9);
const map1 = new Map(set9);
console.log(map1);
Map
相当于复制了新的Map
但map中值是引用类型的还是原来的引用
但map中值是引用类型的还是原来的引用
//传map
const map2 = new Map([
['name', { a: 1, b: 2 }], ['age', 19], ['sex', '男']
]);
const map3 = new Map(map2);
console.log(map3);
console.log(map3 === map2);// ====false
let nnn = map2.get('name');
nnn.a = 1000000;
console.log(map3);
console.log(map2.get('name') === map3.get('name'));// true
Map的注意事项
判断键名是否重复的方式
基本遵循严格相等===,NaN例外,Map中NaN和NaN相等
什么时候使用Map
如果只是使用key->value的形式,或者需要字符串以外的值做键,考虑使用Map。
只有模拟现实世界的实体时,才用对象
只有模拟现实世界的实体时,才用对象
Map实际开发中的应用
见案例
Iterator遍历器和for…of…循环
Iterator是什么
Iterator是遍历器,用来遍历的
遍历的过程
Symbol.Iterator(可遍历对象的生成方法)-> it (可遍历对象)->it.next() ->it.next()->……->直到done为false
寻找数组的Iterator并使用
const it = [1,2][Symbol.Iterator]();
数组的iterator对象
这里的it就是可遍历对象
Iterator解惑
为什么需要Iterator遍历器?
数组可以使用for循环,forEach等,对象可以用for in。Iterator是一个统一的遍历方式
如何更方便的使用Iterator?
我们不会直接去使用Iterator,已经有封装好的方便的用法,底层是用了Iterator
for……of……
认识for……of……
底层是Iterator
const arr = [0, 1, 2, 3, 4, 5];
// for……of……底层使用的Iterator
const it = arr[Symbol.iterator]();
let next = it.next();
while (!next.done) {
console.log(next.value);
next = it.next();
}
只会遍历出done为false时对应的value值
const arr1 = [0, 1, 2, 3, 4, 5];
for (const item of arr1) {
console.log(item);
}
与break、continue一起使用
break终止循环
continue跳出本次循环
在for……of中如何取到数组的索引
const arr2 = [2, 3, 4,5];
for(const key of arr2.keys()){
console.log(key);
}
keys()得到的是索引的可遍历对象
也可以使用values() 得到值的可遍历对象
在for of中同时取到值和索引
const arr4 = [2, 3, 4, 5];
for (const entry of arr4.entries()) {
console.log(entry);// 同时获得下标和值
}
// 可以直接解构赋值
for (const [index,value] of arr4.entries()) {
console.log(index, value);// 同时获得下标和值
}
原生可遍历和非原生可遍历
什么是可遍历
只要有Symbol.Iterator方法,并且这个方法可以生成可遍历对象,就是可遍历的
只要可遍历,就可以通过fo……of循环来统一遍历
原生可遍历
数组
字符串
Set
Map
arguments
NodeList
非原生可遍历
一般的对象
【拓展】Symbol详解
一、什么是Symbol
Symbol 是 ES6 中引入的一种新的基本数据类型,用于表示一个独一无二的值。它是 JavaScript 中的第七种数据类型,与 undefined、null、Number(数值)、String(字符串)、Boolean(布尔值)、Object(对象)并列。
const a = Symbol();
console.log(a); //Symbol()
console.log(typeof a) // 类型是:Symbol
二、Symbol语法规范
1、基本语法
let a = Symbol();
let b = Symbol();
console.log(a); //Symbol()
console.log(b); //Symbol()
console.log(a === b) // false
let b = Symbol();
console.log(a); //Symbol()
console.log(b); //Symbol()
console.log(a === b) // false
let a = Symbol("symbol1");
let b = Symbol("symbol2");
console.log(a); //Symbol("symbol1")
console.log(b); //Symbol("symbol2")
let b = Symbol("symbol2");
console.log(a); //Symbol("symbol1")
console.log(b); //Symbol("symbol2")
可以传入字符串
2、遍历
let s1 = Symbol('a');
let s2 = Symbol('b');
// 由于 s1 和 s2 是一个变量,而不是字符串,因此需要使用中括号括起来(否则它会被当做字符串使用)
let a = {
name: "夕山雨",
[s1]: 24,
[s2]: function(){}
}
let s2 = Symbol('b');
// 由于 s1 和 s2 是一个变量,而不是字符串,因此需要使用中括号括起来(否则它会被当做字符串使用)
let a = {
name: "夕山雨",
[s1]: 24,
[s2]: function(){}
}
以 Symbol 类型的变量作为对象属性时,该属性不会出现在 for … in、for … of循环中(后面小节就会讲解for…of循环的)。
3、Symbol.for(),Symbol.keyFor()
(1)Symbol.for():Symbol 提供的一种可以创建相同 Symbol 的机制,
就是使用 Symbol.for()方法进行注册
就是使用 Symbol.for()方法进行注册
let a = Symbol.for('imooc'); //全局注册了以"imooc"为描述符的 Symbol
//由于描述符"imooc"已被注册到全局,因此这里创建的 Symbol 与上面是同一个
let b = Symbol.for('imooc');
console.log(a === b) // true
//由于描述符"imooc"已被注册到全局,因此这里创建的 Symbol 与上面是同一个
let b = Symbol.for('imooc');
console.log(a === b) // true
(2)Symbol.keyFor():返回一个全局注册的 Symbol 的描述符
let a = Symbol.for('imooc');
let res = Symbol.keyFor(a)
console.log(res) // imooc
let res = Symbol.keyFor(a)
console.log(res) // imooc
三、symbol-的作用
由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。
这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
let s1 = Symbol('s1');
let s2 = Symbol('s2')
const obj = {
age: 16,
age: 19,
[s1]: 'Hello!',
[s2]: 'world'
};
console.log(obj)
let s2 = Symbol('s2')
const obj = {
age: 16,
age: 19,
[s1]: 'Hello!',
[s2]: 'world'
};
console.log(obj)
四、常用内置的-symbol-值:symbol.iterator
对象的 Symbol.iterator 属性,指向该对象的默认遍历器方法 ,凡是具有[Symbol.iterator]方法的对象都是可遍历的,可以使用 for … of循环依次输出对象的每个属性。
数组和类数组,以及 ES6 新增的 Map、Set 等都原生部署了该方法,因此它们都可遍历
数组和类数组,以及 ES6 新增的 Map、Set 等都原生部署了该方法,因此它们都可遍历
五、Symbol 与基本数据类型转换
(1)Symbol 不能转成数字。会报错
(2)可以转成布尔值和字符串
let s1 = Symbol('1');
console.log(typeof s1) // symbol
let str = String(s1)
console.log(typeof str) // string
let bool = Boolean(s1)
console.log(typeof bool) // boolean
console.log(typeof s1) // symbol
let str = String(s1)
console.log(typeof str) // string
let bool = Boolean(s1)
console.log(typeof bool) // boolean
使用了Iterator的场合
数组的展开运算符
只要是可遍历的,都可以按照数组的形式运算展开运算符展开,这就是为什么set、map、字符串、arguments、类数组等可以直接展开
数组的解构赋值
只要是可遍历的,都可以按照数组的形式进行解构赋值,这就是为什么set、map、字符串、arguments、类数组等也可以解构赋值
Set、Map的构造函数
for……of
ES6的新增方法
字符串的新增方法
includes()
字符串中是否包含某些字符
第二个参数:开始搜索的位置,不传,默认为0
padStart()和padEnd()
原字符串不会被截掉,补全字符串长度(从头部开始补全、从尾部开始补全)
'x'.padStart(6,'wy')
没投传补全的字符串,会用空格补全
trimStart()和trimEnd()
去除字符串的首尾空格,字符串中间的空格不会去除
replaceAll()
console.log('aabbcc'.replace('a','x'));// 这里只能替换第一个a为x
console.log('aabbcc'.replace(/a/g,'x'));// 使用正则替换所有的a
console.log('aabbcc'.replaceAll('a','x'));//这样可以替换所有的a为x
console.log('aabbcc'.replaceAll(/a/g,'x'));
console.log('aabbcc'.replaceAll(/a/,'x'));//这里会报错,replaceAll里如果是已正则必须带g
数组的新增方法
includes()
数组中是否含有某个成员
基本遵循严格相等===,但NaN不同,includes中认为NaN相同
第二个参数时起始搜索位置,不传默认为0
Array.form()
把其他数据转化为数组
哪些可以转换为数组
字符串、Set、Map、NodeList、arguments等
拥有length属性的任意对象,只会将键是数字的值转为数组,并且按照数字键的顺序
第二个参数:类似于数组的map()方法,对得到的值做一些处理
// ["h111", "e111", "l111", "l111", "o111"]
console.log(Array.from('hello', value => {
return value + '111';
}));
console.log([1, 2].map(value => value * 2));// [2,4]
第三个参数:给第二个函数传入this指向
// Array.from()第三个参数
Array.from('hello', value => {
console.log(this);// 因为这里是箭头函数没有this,所以还是window
}, document);
Array.from('hello', function () {
console.log(this);// document
}, document);
Array.from('hello', function () {
console.log(this);// {}
}, {});
Array.from('hello', function () {
console.log(this, this[0]);// {'0':'1'} , 1
}, { '0': '1' });
find() 和 findIndex()
找到某个值(找到符合的哪一个立即返回值)、找到某个值对应的索引(找到符合的哪一个立即返回其索引)
第一个参数是回调函数
// 输出3
console.log([1, 2, 3, 4].find((value, index, arr) => {
return value > 2;
}));
回调函数的参数:值value、索引index、数组自身
第二个参数是this指向
同Array.form()的第三个参数,注意箭头函数没有this
对象的新增方法
Object.assign()
合并对象
将属性直接合并到了第一个参数对应的对象中,返回的就是合并后的对象
为了防止第一个参数被修改,一般将一个空对象作为第一个参数
Object.assign({},o1,o2……,on);
注意事项
如果基本类型作为源对象时(源是指第2个参数或者第n个参数,第一个参数时目标对象)
会将基本数据类型转换为对象,再合并。与对象的展开类似
同名属性覆盖
后面的会覆盖前面的
应用
合并用户参数和默认参数
获取对象键值等
Object.keys()
得到对象的键的数组
Object.values()
得到对象键对应值的数组
Object.entries()
得到一个二维数组,二维数组里面的数组,第一个值是键,第二个值是键对应的值
以上3个和数组遍历中的区别
返回值
对象返回的是数组,数组的返回的是可遍历对象Iterator
调用者
数组是实例方法,对象是构造函数方法
使用for……of遍历对象
const person = { name: 'zhangsan', age: 19, sex: 'n' };
for (const key of Object.keys(person)) {
console.log(key);
}
for (const value of Object.values(person)) {
console.log(value);
}
for (const item of Object.entries(person)) {
console.log(item);
}
// 解构赋值
for (const [key, value] of Object.entries(person)) {
console.log(key, value);
}
注:不保证顺序
ES6之Promise
初识Promise
什么是Promise?
异步操作的一种解决方案
在没学习Promise之前,是用回调函数来处理的,两者也会配合使用
什么时候使用Promise?
用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题
Promise的基本使用
实例化构造函数生成实例对象
const p = new Promise(()=>{});
入参是一个回调函数
Promise的状态
有3种状态
一开始是pendding(未完成),执行resolve,变成fulfilled(resolved),已成功
一开始是pendding(未完成),执行reject,变成rejected,已失败
Promise的状态一旦变化,就不会再改变了
const p = new Promise((resolve,reject)=>{
resolve();
reject();
});
先调用了resolve(),已经是fulfilled状态了,后面再调用reject(),状态不会变了
const p = new Promise((resolve,reject)=>{
reject();
resolve();
resolve();
});
先调用了reject(),已经是rejected状态了,后面再调用resolve(),状态不会变了
then()方法
入参是两个回调函数,第一个是fulfilled的时候调用的,第二个是rejected时候调用de
resolve()、reject()函数的参数
resolve的参数,就是then第一个回调函数里可以接受的参数,比如reject参数可以是new的一个Error对象
Promise的实例方法
then()
什么时候执行?
当pendding->fulfilled,执行then的第1个回调函数
当pendding->rejected,执行then的第2个回调函数
执行后返回值是什么?
then方法执行之后返回一个新的Promise对象,和原来的Promise对象没有关系
then一般会连写
返回的Promise对象的状态如何改变?
then默认返回的是成功状态的Promise对象
在then回调函数体中,return后面的东西会使用Promise包装一下
向后传值
使用Promise解决回调地狱问题
catch()
一般用then()只处理成功的状态,使用catch()方法可以专门用来处理rejected状态,本质上是then的特例
then(null,err=>{});
catch可以捕获前面的错误,一般总是建议,Promise对象后面要跟catch,这样可以处理Promise内部发生的错误
finally()
也是then的特例
什么时候会执行?
当Promise的状态发生变化的时候,不论如何变化都会执行,不变化不执行
Promise的几个构造方法
Promise.resolve()
本质
Promise.resolve();
成功状态的一种简写形式
new Promise(resolve=>{
resolve();
});
new Promise(resolve => resolve())
简写:省略大括号
参数
一般参数
Promise.resolve('参数').then((data)=>{
console.log(data);// 输出上面传入的参数
});
Promise对象作为参数
当Promise.resolve接收的参数是Promise对象时,什么都不做,直接返回这个对象
const prom = new Promise((r) => {
setTimeout(r, 2000, '我是参数');
});
以下两种写法是等价的
Promise.resolve(prom).then((data) => {
console.log(data);
});
prom.then((data) => {
console.log(data);
});
console.log(Promise.resolve(prom) === prom);// true
Promise实例的resolve()方法,如果是结构的Promise对象,也是根据传入的Promise对象的状态变化来决定then执行哪一个回调
具有then方法的对象
(用的不多)
(用的不多)
// 具有then方法的对象作为参数
const obj = {
then() {
console.log('我是有then方法的对象');
}
};
// 首先传入的obj,会先直接调用一下obj里面的then方法。
//后面的then里面的两个回调都不会执行,因为调用之后生成的Promise对象是pendding状态
// console.log(Promise.resolve(obj));// pendding状态
Promise.resolve(obj).then(
(data) => {
console.log('success-我被调用了', data);
},
(data) => {
console.log('error-我被调用了', data);
}
);
// 具有then方法的对象作为参数
// 但将then方法写的像Promise对象,两个入参,调用一下第一个,后面跟的then就会执行第一个回调
// 调用一下第二个函数,后面跟的then就会执行第二个回调
const obj1 = {
then(r1, r2) {
console.log('我是有then方法的对象-obj1');
r1('success');
//r2('error');
}
};
// 首先传入的obj1,会先直接调用一下obj1里面的then方法。
Promise.resolve(obj1).then(
(data) => {
console.log('success-我被调用了', data);
},
(data) => {
console.log('error-我被调用了', data);
}
);
在then方法中的应用
Promise.reject()
本质
Promise.reject();
失败状态的一种简写形式
new Promise((resolve,reject)=>{
reject();
});
new Promise((resolve,reject) => reject())
简写:省略大括号
参数
不管什么参数,都会原封不动的向后传送,作为后续方法的参数
在then方法中的应用
Promise.all()
作用
用来关注多个Promise对象的状态的变化
传入多个Promise实例,包装成一个新的Promise实例返回
基本用法
子主题
Promise.all()的状态与传入的Promise实例对象状态有关。
所有的状态都变成resolved,最终才是resolved;
只要有一个是rejected,最终状态就是rejected
所有的状态都变成resolved,最终才是resolved;
只要有一个是rejected,最终状态就是rejected
Promise.race()
race就是竞赛的意思
Promise.race()的状态取决于第一个完成的Promise对象,
第一个完成的成功了,那最终状态就是成功;
第一个失败了,那最终状态就是失败
第一个完成的成功了,那最终状态就是成功;
第一个失败了,那最终状态就是失败
Promise.allSettled()
Promise.allSettled()与传入的Promise对象的状态无关,永远都是成功的。它只会忠实的记录下每个Promise的表现
Promise.any()
传入的参数是一组Promise实例,那么所有Promise实例都变成rejected状态,返回的Promise才会变成rejected状态
参数中只要有一个Promise改变为成功状态,则返回的Promise状态就是成功的状态。
只要有一个成功就会终止
参数中只要有一个Promise改变为成功状态,则返回的Promise状态就是成功的状态。
只要有一个成功就会终止
(1) Promise.all() 会返回一组完成值那样,而Promise.any()只能得到一个成功值(假设至少有一个 promise 完成)。当我们只需要一个 promise 成功,而不关心是哪一个成功时此方法很有用的。
(2)Promise.race() 总是返回第一个结果值,而Promise.any()返回的是第一个成功的值。这个方法将会忽略掉所有被拒绝的promise,直到第一个 promiee 成功。
(2)Promise.race() 总是返回第一个结果值,而Promise.any()返回的是第一个成功的值。这个方法将会忽略掉所有被拒绝的promise,直到第一个 promiee 成功。
使用场景
实际开发中,可能会有这样的需求:一次性加载多张图片,哪一张先加载出来就显示哪一张。那么此时就可以使用Promise.any()方法实现效果。
Promise的注意事项和应用
resolve和reject执行后的代码
如果调用了resolve()或者reject()之后,如果后面还有代码,在return之前的代码是会执行的
建议在resolve()或者reject()前加上return,不再执行后面的代码,后面的代码放在后续的then里执行
Promise.all()、Promise.race()、Promise.allSettled()等参数的问题
参数如果不是Promise对象数组,会将不是Promise对象的数组元素转化为Promise对象
// 如果参数不是Promise对象,会包装成Promise
Promise.all([1, 'woshi', 3]).then((data) => {
console.log(data);
});
//等价于
Promise.all([
Promise.resolve(1),
Promise.resolve('woshi'),
Promise.resolve(3),
]).then((data) => {
console.log(data);
});
不只是数组,任何可遍历的对象都可以传入
Promise.all()、Promise.race()、Promise.allSettled()等的错误处理
错误既可以单独处理,也可以统一处理,错误一旦被处理,就不会在其他地方再处理一遍
【扩展】async/await
什么是async/await
async/await是基于Promise实现的。
async/await使得异步代码看起来像同步代码。
以前的方法有回调函数和Promise,,async/await是写异步代码的新方式。
async/await语法规范
async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。
当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
const timeout= time => {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve();
}, time);
})
};
const start = async () => {
// 在这里使用起来就像同步代码那样直观
console.log('start');
await timeout(3000);
console.log('end');
return "imooc"
};
start().then(()=> {
console.log('test....')
});
注意事项
1、await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。
function fn() {
await timeout(300);// 报错, await is only valid in async functions and the top level bodies of modules
};
fn();
// 可以这样用
async function fn() {
await timeout(300);
}
fn();
2、await 后面跟着是一个Promise对象, 会等待Promise返回结果了,再继续执行后面的代码。
//2、await 后面跟着是一个Promise对象, 会等待Promise返回结果了,再继续执行后面的代码。
//执行下面的代码,控制台等待3秒后,输出imooc,然后才输出后面的console.log语句中的end,如下图所示:
const timeout1 = time => {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('imooc');
resolve();
}, time);
})
};
const start1 = async () => {
await timeout1(3000);
console.log('end');
};
start1();
3、await 后面跟着的是一个数值或者字符串等数据类型的值,则直接返回该值
// 3、await 后面跟着的是一个数值或者字符串等数据类型的指,则直接返回该值
const num = 1
const str = "imooc";
const arr = [1, 2];
const start2 = async () => {
console.log(await num)
console.log(await str)
console.log(await arr)
};
start2();
4、await后面跟着的是定时器,不会等待定时器里面的代码执行完,而是直接执行后面的代码,然后再执行定时器中的代码。
//4、await后面跟着的是定时器,不会等待定时器里面的代码执行完,而是直接执行后面的代码,然后再执行定时器中的代码。
const start4 = async () => {
console.log(1)
await setTimeout(() => {
console.log(2)
}, 1000);
console.log(3);
};
start4();// 1 3 2
错误捕获
可以直接用标准的try catch语法捕捉错误
ES6之Class类
初识class
什么是class
人类:类
具体的人:实例/对象
具体的人:实例/对象
类可以看做事对象的模板,用一个类可以创建出多个不同的对象
JS中的类建立在原型上
class基本用法
实例化时会执行构造方法,所以必须有构造方法
但可以不写出来,不写就用默认的构造方法
但可以不写出来,不写就用默认的构造方法
//实例化时会执行构造方法,所以必须有构造方法,但可以不写出来,不写就用默认的构造方法
class Person {
constructor() {
console.log('我是构造方法,实例化时会被调用');
}
}
// 实例化
const p = new Person();
一般在构造方法中定义属性,不再构造方法中定义方法
(因为每次创建一个对象就会创建一个方法,创建出来的方法并不是同一个引用,会浪费大量内存)
(因为每次创建一个对象就会创建一个方法,创建出来的方法并不是同一个引用,会浪费大量内存)
//实例化时会执行构造方法,所以必须有构造方法,但可以不写出来,不写就用默认的构造方法
class Person {
// 这里可以传入实例化的参数
constructor(name, age, sex) {
console.log('我是构造方法,实例化时会被调用');
this.name = name;
this.age = age;
this.sex = sex;
}
// 这样写是各实例共享的方法
speak() {
console.log('speak', '我是' + this.name);
}
fun() {
}
}
// 实例化
const zs = new Person('zhangsan', 18, '男');
console.log(zs.name, zs.age, zs.sex);
zs.speak();
class两种定义形式
声明形式
class Person {
constructor(name, age, sex) {
……
}
}
constructor(name, age, sex) {
……
}
}
表达式形式
将一个匿名的类赋值给一个常量
const Student = class {
constructor(id,score){
}
}
立即执行的class
new (class {
constructor() {
console.log(111);
}
})();
class与构造函数区别
class的typeof是function
class底层是由构造函数实现的
class中添加的方法(不是在构造方法中添加的)就是在原型上的方法
Class的属性和方法
实例属性
// 实例属性
class Cat {
// 这里添加属性,不需要加this,可以设置一些默认值,赋值好右边可以是方法
name = 'miaomiao';
age = 10;
sex = 'male';
say = function () {
console.log('我是' + this.name);
}
constructor(name, age) {
this.name = name;
this.age = age;
}
speak() {
console.log('我是' + this.name);
}
}
const miao = new Cat('miaomiaomiao', 5);
const miao1 = new Cat('miaomiaomiao', 5);
console.log(miao.name, miao.age, miao.sex);
console.log(miao.say === miao1.say);// false
console.log(miao.speak === miao1.speak);// true 这里的方法是同一个
静态方法
静态方法就是类的方法
在class中使用static关键字修饰,
可以和实例方法同名不会冲突,
直接用类打点调用,不需要实例化
可以和实例方法同名不会冲突,
直接用类打点调用,不需要实例化
class Person{
static say(){
}
}
static say(){
}
}
Person.say=function (){
}
}
但不是很推荐这种写法
静态方法中的this指向的是类本身,而不是实例化出来的对象
静态方法通常用于为一个应用程序创建工具函数。
静态属性
静态属性就是类的属性,
要和实例属性区分开
要和实例属性区分开
class Dog {
// 不要这么写,目前这个只是提案,有兼容性问题
static version = '1.0';
// 可以使用静态方法的形式
static getVersion() {
return '1.0';
}
}
console.log(Dog.getVersion());
私有属性和方法
为什么需要私有属性和方法
声明的类的属性和方法只能在类内部使用的,称之为私有
一般情况下,类的属性和方法都是公开的
目前没有支持的只能在类内部使用的属性和方法,需要模拟
模拟私有属性和方法
下划线开头表示私有,一种约定。
在定义一个方法返回这个属性,类外部通过调用这个方法获取
在定义一个方法返回这个属性,类外部通过调用这个方法获取
并不能强制不能再类外访问,还是可以访问的,只是约定俗成的不去访问
江苏有属性和方法移出类
将类的声明写在一个IIFE中,私有属性或方法定义在类外,将类赋值给window;
在另一个IIEF中实例化类。
在另一个IIEF中实例化类。
Class的继承
extends
子类继承父类
使用extends会继承父类的:属性、实例方法、静态方法、静态属性等。
// 继承
class Programmer extends Person {
constructor(name, age) {
super(name, age);
}
}
子类改写继承的属性或方法
子类中写了和父类同名的方法,会同名覆盖。
子类中新增的,只有子类有。
子类中新增的,只有子类有。
super
作为函数调用
代表父类的构造方法,只能用在子类的构造方法中,在其他地方使用会报错。这种情况,在子类中调用super相当调用父类的构造方法
super在子类中,this指向的是子类的实例,而不是父类
作为对象使用
在构造方法中或非静态方法中使用
此时super代表父类的原型对象,Parent.prototype
此时super代表父类的原型对象,Parent.prototype
可以在子类中调用父类的方法
通过super调用父类的非静态方法时,方法内部的this指向的是当前子类的实例
定义在父类实例上的方法和属性,使用super在子类中不能访问到
在静态方法中使用
此时super代表父类本身而不是原型对象也不是实例
此时super代表父类本身而不是原型对象也不是实例
可以使用子类调用父类的静态方法
通过super调用父类的静态方法时,方法内部的this指向的是当前子类本身而不是子类实例
注意事项
使用super时,需要显性的指出是作为函数还是作为对象使用,否则会报错
当子类继承父类时,如果不需要通过constructor设置属性和继承父类constructor中的属性,那么就可以不写constructor和super,否则,就必须写上constructor和super。
Class实际开发中的应用
幻灯片
ES6之Module(模块)
初识Module
Module是什么
什么是模块
一个一个的局部作用域的代码块
什么是模块系统
模块系统解决的问题
解决模块化的问题
消除全局变量
管理加载顺序
Module的基本用法
使用Module模块化之前的例子
使用script标签加载标签
一个文件就是一个模块了
只要你会用到import或export,在script标签加载时候,就要加上type="module"
<script src="./js/index.js" type='module'></script>
<script type='module'>
import './module-0.js';
</script>
import './module-0.js';
</script>
Module如何解决模块化的问题的
Module的导入和导出
认识导出和导入
导出的东西可以被导入(import),并访问到
一个模块没有导出,也可以被导入,被导入的代码都会被执行一遍,多次导入,也仅执行一遍
import './js/base.js';
export default 和对应的import
export default导出一个默认值
export default XXXX;
import xxxx from './js/xxxxxxx.js';
import xxxx from './js/xxxxxxx.js';
一个模块只能有一个export default
import和export default对应使用的时候,import可以随便取别名,不一定和export default后的一样
export和对应的import
基本用法
export后面跟声明或语句
export的两种写法
export const name = 'zhangsan';
const name='zhangsan';
export {name};
export {name};
import的写法
import {name} from './js/xxxxxx.js';
这里的name必须和上面的export后的名字一样,不能随意取名
多个导出多个导入
const name = 'zhangsan';
const fun = function (){};
class Person{}
const p = { d: "ddd", e: "eee" };
export { name, fun, Person, p };
const fun = function (){};
class Person{}
const p = { d: "ddd", e: "eee" };
export { name, fun, Person, p };
import { fun, name, Person, p } from './module.js';
可以不按照导出的顺序,只要名字对应上即可
导入导出时起别名
使用as取别名
import { func as say, username, Person, person } from './module.js';
export { name as username, fun as func, Person, p as person };
整体导入
import * as obj from './module-0.js';
使用*号导入整体,使用as取个别名,这种写法也会导入xeport default导出的内容
同时导入
同时导入export以及export default所导出的内容
import age, { func as say, username, Person, person } from './module.js';
前面的age是export defaul所导出的,一定要将这个放在前面,否则报错
Module的注意事项
模块顶层的this指向
在模块中,顶层的this指向undefined
可以利用这个特性,如果script标签没有使用type='module'那就会按照普通模式加载而不是模块化了,可以在js模块中判断,顶层this是否是undefined,如果不是,就要给出提示,按照模块化加载js
import关键字和import() 函数
import命令具有提升效果,会提升到整个模块的头部,率先执行
import执行的时候,代码还没有执行。import和export命令,只能用在模块的顶层,不能在代码块中执行
import执行的时候,代码还没有执行。import和export命令,只能用在模块的顶层,不能在代码块中执行
比如不能放在if判断中
import()函数可以实现按条件导入
import()返回的是一个Promise对象,后面可以跟then() catch()等
const PC = true;
const Mobile = false;
if (PC) {
import('./module.js').then(
() => {
console.log('加载PC端js成功');
},
……
);
} else if (Mobile) {
import('./module-0.js');
}
目前import()只是一个提案,可能存在兼容性问题,后续可以结合webpack使用
导入导出的复合写法
可以从一个模块导入然后再导出给其他模块使用,相当于一个中转站
但复合写法导出的无法在当前模块使用
export { version } from "./base.js";
相当于
import {version} from './base.js';
export {version};
export {version};
Module在实际开发中的应用
将默认值等单独拆出一个模块文件,然后将自己导出给其他模块用
将所有常量可以单独拆出一个模块文件,然后将自己导出给其他模块用
相对独立的功能都可以拆出来,注意this
如何更合适的拆分,需要多练多用
ES6之Babel与Webpack
Babel
Bable是什么
认识Babel
Babel是Javascript的编译器,用来将ES6的代码转换为ES6之前版本的代码(ES3、ES5等)
官网:https://babeljs.io/
在线编译:https://babeljs.io/repl
使用Babel
Babel本身可以编译ES6的大部分代码,比如let、const、箭头函数、类等。
但是对于ES6新增的API,如Set、Map、Promise等全局对象,以及定义在全局对向上的方法(如Object.assign\Array.fromd等),都不能直接编译,需要借助其他模块
但是对于ES6新增的API,如Set、Map、Promise等全局对象,以及定义在全局对向上的方法(如Object.assign\Array.fromd等),都不能直接编译,需要借助其他模块
Babel一般要配合Webapck来编译模块(Module)语法
解释编译结果
Babel的使用方式
推荐CLI和Webpack结合
在命令行工具中使用Babel
Babel使用前的准备工作
https://babeljs.io/setup#installation
https://babeljs.io/setup#installation
什么是Node.js和npm
Node.js
Node.js是一个平台或者工具,对应浏览器。在这个平台上使用的语言是Node.js,一种后端的Javascript
后端的Javascript=ECMAScript+IO+FIle+……+等服务端的操作
通俗的,一般将后端的Javascript成为Node.js
npm
node包管理工具
安装Node.js
下载包并安装
会同时安装好Node 以及npm
会同时安装好Node 以及npm
node -v
npm -v
官网:https://nodejs.org/en/
初始化项目
在项目目录中
1、npm init
初始化项目
2、初始化之后,会在项目目录下生成一个package.json
使用npm安装包的时候会将安装的情况记录在这个文件中。
比如更换开发环境、更换电脑,不需要将下载的包复制一份,只需要有这个文件就可以了
可以再使用一些命令,将安装过的包全部再安装一遍就好了
比如更换开发环境、更换电脑,不需要将下载的包复制一份,只需要有这个文件就可以了
可以再使用一些命令,将安装过的包全部再安装一遍就好了
安装Babel需要的包
npm config set registry https://registry.npm.taobao.org
只执行一遍,切换安装源,从国外切换到国内
测试:npm config get registry
根据官网的提示执行:
npm install --save-dev @babel/core @babel/cli
npm install --save-dev @babel/core @babel/cli
--save-dev是指开发依赖
执行完之后会在刚刚的package.json中增加以下内容
"devDependencies": {
"@babel/cli": "^7.15.4",
"@babel/core": "^7.15.4"
}
"@babel/cli": "^7.15.4",
"@babel/core": "^7.15.4"
}
指向完成后会在当前项目目录下生成安装包的目录node_modules
如果这个包丢失,但package.json文件保留,可以在当前项目目录下执行npm install 可以下载package.json中指定的所有包
如果这个包丢失,但package.json文件保留,可以在当前项目目录下执行npm install 可以下载package.json中指定的所有包
可以使用@安装指定版本
npm install --save-dev @babel/core@7.11.0 @babel/cli@7.10.5
npm install --save-dev @babel/core@7.11.0 @babel/cli@7.10.5
使用Babel编译ES6代码
编译的命令
根据官网提示添加
"scripts": {
"build": "babel src -d dist",
},
"build": "babel src -d dist",
},
这里的build就是后面一行命令的一个简写,取名字
src指ES6的代码目录
-d dist => -out-dir dist
输出到dist目录
Babel配置文件
根据官网提示添加
安装Babel的包
npm install @babel/preset-env --save-dev
创建Babel配置文件
为了启用预设,您必须在babel.config.json文件中定义它,如下所示:
{
"presets": ["@babel/preset-env"]
}
{
"presets": ["@babel/preset-env"]
}
Webpack
https://www.webpackjs.com/
https://www.webpackjs.com/
Webpack入门
Webpack是什么
认识Webpack
Webpack是静态模块打包器,当Webpack处理应用程序时,会将所有这些模块打包成一个或多个文件
ES6中的Module语法,就是用webpack来处理
Webpack中的模块是什么?
可以理解为:Webpack可以处理的最小单位
ES6中的Module模块,属于Webpack模块。
Webpack还可以处理css/js/图片、图标字体等单位。----都可以称之为Webpack模块
Webpack还可以处理css/js/图片、图标字体等单位。----都可以称之为Webpack模块
Webpack中静态是指什么?
开发过程中存在于本地的css/js/图片、图标字体等文件,就是静态的
动态的内容(比如图片再服务器端存放),Webpack没法处理,只能处理静态的
Webpack初体验
初始化项目
npm init
安装Webpack需要的包
npm install --save-dev webpack-cli@3.3.12 webpack@4.44.1
配置Webpack
配置文件
webpack.config.js
配置文件内容
"webpack": "webpack --config webpack.config.js"
打包并测试
子主题
Webpack核心概念
entry和output
entry
指定入口文件
单个入口文件
entry: "./src/index.js",
多个入口文件
entry: {
main:'./src/index.js',
search:'./src/search.js'
},
main:'./src/index.js',
search:'./src/search.js'
},
output
指定出口
单入口文件对应的输出
const path = require("path");
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
__diename只当前项目目录,绝对路径
resolve是拼接自己的两个参数
filename,指定输出文件名字
多入口对应的输出
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
中括号[name]就是entry多个入口文件对应的最前面的名字
loader
什么是loader
loader就是让Webpack能够去处理非JS模块。loader被用于转换某些类型的模块
学会从官网查找更多的loader使用配置。官网地址:https://www.webpackjs.com/loaders/
babel-loader
安装babel-loader
npm install --save-dev @babel/core@7.11.0 @babel/preset-env@7.11.0
这俩也是需要安装的,如果已经安装了就不需要了
npm install --save-dev babel-loader@8.1.0
创建babel配置文件
引入core-js模块,用来编译ES6新增的PAI,比如Promise等
这样引入编译后的js文件在IE浏览器中测试时能通过的
这样引入编译后的js文件在IE浏览器中测试时能通过的
官网地址:https://babeljs.io/docs/en/babel-polyfill
npm install --save-dev core-js@3.6.5
在我们的源码入口js文件头部增加:
import "core-js/stable";
import "core-js/stable";
打包测试
npm run ……
plugins
什么是plugins
官网地址:https://www.webpackjs.com/plugins/
loader被用于转换某些类型的模块,而插件可以用于执行范围更广的任务。
插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量
以html-webpack-plugin为例学习
初始化项目
安装html-webpack-plugin插件
npm install --save-dev html-webpack-plugin@4.3.0
单页面-配置html-webpack-plugin插件(webpack配置文件)
// 导入html-webpack-plugin插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
plugins: [
// 如何使用插件,这里实例化一下上面定义的
new HtmlWebpackPlugin({
template: "3.html",
}),
],
// 如何使用插件,这里实例化一下上面定义的
new HtmlWebpackPlugin({
template: "3.html",
}),
],
多入口页面-配置html-webpack-plugin插件(webpack配置文件)
entry: {
main: "./src/index.js",
search: "./src/search.js",
},
main: "./src/index.js",
search: "./src/search.js",
},
plugins: [
// 多入口文件--实例化多次
new HtmlWebpackPlugin({
template: "index.html",
filename:'index.html', //指定生成出的文件名
// 指定需要引入的哪个js文件,对应entry里的前面那个名字
chunks:['main']
}),
new HtmlWebpackPlugin({
template: "search.html",
filename:'search.html',
chunks:['search']
}),
],
// 多入口文件--实例化多次
new HtmlWebpackPlugin({
template: "index.html",
filename:'index.html', //指定生成出的文件名
// 指定需要引入的哪个js文件,对应entry里的前面那个名字
chunks:['main']
}),
new HtmlWebpackPlugin({
template: "search.html",
filename:'search.html',
chunks:['search']
}),
],
Webpack的应用
处理css文件
1、以style标签形式嵌入html页面
需要安装css-loader并配置
npm install --save-dev css-loader@4.1.1
webpack只会处理js文件,js以外的文件借助loader处理
需要安装style-loader并配置
npm install --save-dev style-loader@1.2.1
2、抽取css为单独的文件,html使用link引入
也需要安装css-loader并配置
npm install --save-dev css-loader@4.1.1
webpack只会处理js文件,js以外的文件借助loader处理
需要使用插件mini-css-extract-plugin并配置
npm install --save-dev mini-css-extract-plugin@0.9.0
new MiniCssExtractPlugin({
// [name] 表示取entry中的名字,没有默认为main
filename: "css/[name].css",
}),
// [name] 表示取entry中的名字,没有默认为main
filename: "css/[name].css",
}),
使用file-loader处理css中的图片
如果图片是服务器的图片,不需要webpack处理。正常就可以执行
需要安装file-loader并配置
npm install --save-dev file-loader@6.0.0
module: {
rules: [
// 处理css的loader
{
test: /\.css$/,
// 一个loader是这种写法,多个loader就需要用use
// loader:'css-loader'
// 多个loader,注意顺序,这里会从右往左加载
// MiniCssExtractPlugin提供loader,是这种用法
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
// 处理图片的loader
{
test:/\.(png|jpg|gif)$/,
use:['file-loader']
}
],
},
rules: [
// 处理css的loader
{
test: /\.css$/,
// 一个loader是这种写法,多个loader就需要用use
// loader:'css-loader'
// 多个loader,注意顺序,这里会从右往左加载
// MiniCssExtractPlugin提供loader,是这种用法
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
// 处理图片的loader
{
test:/\.(png|jpg|gif)$/,
use:['file-loader']
}
],
},
file-loader将图片从源码复制了一份到输出目录,并重新取了名字
并修改了css引入图片的路径
并修改了css引入图片的路径
module: {
rules: [
// 处理css的loader
{
test: /\.css$/,
// 一个loader是这种写法,多个loader就需要用use
// loader:'css-loader'
// 多个loader,注意顺序,这里会从右往左加载
// MiniCssExtractPlugin提供loader,是这种用法
// 因为这里处理了css到单独的css文件夹下,导致图片处理的时候找错目录
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// 统一加上../
publicPath: "../",
},
},
"css-loader",
],
},
// 处理图片的loader
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "file-loader",
options: {
// 将图片统一输出到images目录下,[name].[exp]表示保留图片自己的名字和后缀
name: "images/[name].[ext]",
},
},
],
},
],
},
rules: [
// 处理css的loader
{
test: /\.css$/,
// 一个loader是这种写法,多个loader就需要用use
// loader:'css-loader'
// 多个loader,注意顺序,这里会从右往左加载
// MiniCssExtractPlugin提供loader,是这种用法
// 因为这里处理了css到单独的css文件夹下,导致图片处理的时候找错目录
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// 统一加上../
publicPath: "../",
},
},
"css-loader",
],
},
// 处理图片的loader
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "file-loader",
options: {
// 将图片统一输出到images目录下,[name].[exp]表示保留图片自己的名字和后缀
name: "images/[name].[ext]",
},
},
],
},
],
},
使用html-withimg-loader处理html中的图片
需要安装html-withimg-loader并配置
npm install --save-dev html-withimg-loader@0.1.16
module: {
rules: [
// 处理css的loader
{
test: /\.css$/,
// 一个loader是这种写法,多个loader就需要用use
// loader:'css-loader'
// 多个loader,注意顺序,这里会从右往左加载
// MiniCssExtractPlugin提供loader,是这种用法
// 因为这里处理了css到单独的css文件夹下,导致图片处理的时候找错目录
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// 统一加上../
publicPath: "../",
},
},
"css-loader",
],
},
// 处理图片的loader
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "file-loader",
options: {
// 将图片统一输出到images目录下,[name].[exp]表示保留图片自己的名字和后缀
name: "images/[name].[ext]",
// 处理html中的图片需要file-loader配合使用,这里不开启ES6的module模块,否则
// html中img的src就是这样子的:src={"default":"images/0.jpg"}
//设置成false就是 src="images/0.jpg"
esModule: false,
},
},
],
},
// 处理html中的img标签
{
test: /\.(html|html|shtml)$/,
use: [
{
loader: "html-withimg-loader",
},
],
},
],
},
rules: [
// 处理css的loader
{
test: /\.css$/,
// 一个loader是这种写法,多个loader就需要用use
// loader:'css-loader'
// 多个loader,注意顺序,这里会从右往左加载
// MiniCssExtractPlugin提供loader,是这种用法
// 因为这里处理了css到单独的css文件夹下,导致图片处理的时候找错目录
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// 统一加上../
publicPath: "../",
},
},
"css-loader",
],
},
// 处理图片的loader
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "file-loader",
options: {
// 将图片统一输出到images目录下,[name].[exp]表示保留图片自己的名字和后缀
name: "images/[name].[ext]",
// 处理html中的图片需要file-loader配合使用,这里不开启ES6的module模块,否则
// html中img的src就是这样子的:src={"default":"images/0.jpg"}
//设置成false就是 src="images/0.jpg"
esModule: false,
},
},
],
},
// 处理html中的img标签
{
test: /\.(html|html|shtml)$/,
use: [
{
loader: "html-withimg-loader",
},
],
},
],
},
使用file-loader处理js中的图片
方式同file-loader处理css图片,只不过是在js中给dom元素img设置src
在js中导入图片
在js中导入图片
import imgUrl from "./images/1.jpg";
使用url-loader处理图片
处理一些base64的图片,功能包含file-loader,后续可以统一使用url-loader
npm install --save-dev url-loader@4.1.0
url-loader是以file-loader为基础的,所以file-loader包也需要下载
配置和file-loader相同,功能相似,可以处理css、js图片,但不能处理html引入的图片。多了一个可以base64编码的功能
//url-loader配置,小于10K的图片都会转成base64的格式
limit:10000
limit:10000
使用Webpack-dev-server搭建开发环境
安装webpack-dev-server
npm install --save-dev webpack-dev-server@3.11.0
收藏
0 条评论
下一页
为你推荐
查看更多