ES6的兼容性:
- 主流浏览器的最新版本几乎全部支持 ES6
- IE 老版本等不支持的浏览器,可以使用 Babel 转码
let 和 const 是什么:
- let 和 const 是用来声明变量或常量的关键字
- let 代替 var 声明变量
- const 是 constant 的缩写,用来声明常量
什么是变量,什么是常量:
- var 、let 声明的就是变量,变量一旦初始化后,还可以重新赋值
- const 声明的是常量,常量一旦初始化,就不能重新赋值了,否则就会报错
为什么需要 const:
- const 是为了防止那些一旦初始化就不希望重新赋值的情况设计的
使用 const 的注意事项:
- 使用 const 声明常量,一旦声明,就必须立即初始化,不能留到后面赋值
- const 声明的常量,允许在不重新赋值的情况下修改它的值,例如:对象可以通过对象.属性的方式赋值
var a = 1;
// 很多行代码之后
...
var a = 2;
console.log(a); // 2 不会报错
let b = 1;
...
let b = 3;
console.log(b); // 会报错
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 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;
}
// 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
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
模版字符串与普通字符串的区别:
- 和其它东西一起使用的时候,使用模版字符串方便注入
- 其它情况下使用模版字符串和一般字符串美多大区别
var xiaoming = {
name: '小明',
age: 12,
sex: '男'
};
// 普通字符串拼接
var info = "我叫" + xiaoming.name + ",今年" + xiaoming.age + "岁了";
// 模版字符串拼接
var info = `我叫${xiaoming.name},今年${xiaoming.age}岁了`;
输出多行字符串:
- 模版字符串中,所有的空格、换行或缩进都会保留在输出之中
// 一般字符串
const info1 = '第一行\n第二行';
// 模版字符串
const info2 = `第一行\n第二行`;
// 或
const info3 = `第一行
第二行`;
如何输出 ` 和 \ 等特殊字符:
- 在想要输出的特殊字符前加反斜杠\
console.log('\"'); // 输出:"
console.log('\\'); // 输出:\
模版字符串的注入(什么东西可以放进${}):
- 只要最终可以得出一个值的就可以通过 ${} 注入到模版字符串中
箭头函数的结构:
- const/let 函数名 = 参数 => 函数体
- const 函数名 = (参数)=>{函数体};
- 由于箭头函数是匿名函数,不能直接使用,所以需要赋值给一个变量或常量
如何将一般函数改写成箭头函数:
- 原来函数的形式有声明形式和函数表达式的形式
// 声明形式
function fun() {}
// 函数表达式形式
const fn = function() {}
单个参数:
- 单个参数可以省略圆括号
- 无参数或多个参数不能省略圆括号
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
});
console.log(this); // window
function fun() {
console.log(this);
}
// 严格模式下指向 undefined
// 非严格模式下指向 window
fun();
const a = {
fun: ()=>{
console.log(this);
}
};
// 箭头函数没有自己的this,然后向外层寻找
// 首先到对象 a,由于 对象 a 不是作用域,于是继续向外
// 然后到全局作用域,全局作用域的 this 是 window
a.fun(); // 指向 window
// 构造函数
// function People(name, age, sex) {
// this.name = name;
// this.age = age;
// this.sex = sex;
// }
const Person = () => {};
new Person();
document.onclick = function() {
console.log(this);
};
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
数组的解构赋值之原理:
- 模式(结构)匹配:赋值符号左右两边的结构相同
- 索引值相同的完成赋值:相同结构的对应位置的变量与值一一对应
- 不想获取的值,可以直接用逗号跳过
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] = [];
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
对象的解构赋值之原理:
- 模式(结构)匹配:赋值符号左右两边的结构相同
{} = {}
// 正常写
// 左边的值可以取别名,如下: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
其它数据类型的解构赋值之字符串:
- 字符串既可以用数组形式解构赋值
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 无法转为对像,所以对它们进行解构赋值,都会报错(无论是采用数组还是对象的形式都会报错)
对象字面量是什么:
- 实例化构造函数生成对象
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,
};
const person = {
// sayHello: function() {},
// 可以简写为
sayHello() {},
};
方括号语法的用法: 方括号语法可以写在对象字面量中
const prop = 'age';
const person = {};
// 原来想将 age 作为 person 的属性
person[prop] = 12;
// ES6增强后,现在想将 age 作为 person 的属性
const person = {
[prop]: 12,
};
方括号中可以放什么: 类似于 ${},值或通过计算(表达式)得到值的都可以放
方括号语法和点语法的区别: 点语法是方括号语法的特殊形式
- 属性名是合法标识符的时候可以使用点语法
- 其它情况下使用方括号语法
const person = {};
person.age 等价于 person['age']
函数参数默认值是什么: 调用函数的时候传参,就使用传递的参数,如果没传参,就用默认值
函数参数默认值的基本用法: 类似于解构赋值默认值,可以直接在行参上赋默认值
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'});
const hello = ({username = 'zhangsan', age = 0, sex = 'male'}) => {
console.log(username,age,sex);
};
hello({username: 'lgk'});
const hello = ({username = 'zhangsan', age = 0, sex = 'male'} = {}) => {
console.log(username,age,sex);
};
什么是剩余参数:
- 剩余参数用“…合法标识符”表示,一般使用“…args”表示
- 剩余参数在使用时只要写 …后面的参数名 即可
- 剩余参数永远是一个数组,即使没有值也是空数组
- 剩余参数区别于固定行参,只有当所传入的实参个数超过固定行参个数,多出的实参放在剩余参数数组中
const fun = (a,b,...args) => {
console.log(a,b,args);
};
剩余参数的注意事项:
- 箭头函数的剩余参数:箭头函数的参数部分即使只有一个剩余参数,也不能省略圆括号
const fun = (...args) => {};
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}
认识展开运算符:
- 由于一些方法不能直接接收数组作为参数,所以可以使用展开运算符将数组展开为参数列表形式
- 展开运算符和剩余参数很像,也是在前面加 “…”
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"]
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({...{}}); // {}
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'}
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
const set = new Set();
set.add(1);
set.add(2).add(3).add(4);
console.log(set); // {1, 2, 3, 4}
const set = new Set();
set.add(1);
set.add(2).add(3).add(4);
console.log(set.has(3)); // true
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); // {}
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}
// 字符串
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}
// 数组去重
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
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"}
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
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
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);
const map = new Map();
map.set('name','zhangsan');
map.set('age',12).set('sex','male');
map.clear();
console.log(map); // {}
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);
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
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
- 只有模拟现实的实体时,才使用对像
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 的构造函数
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
}
什么是可遍历:
- 只要有 Symbol.iterator 方法,并且这个方法可以生成可遍历对像,就是可遍历的
- 只要是可遍历,就可以使用 for … of 循环来统一遍历
原生可遍历:
- 数组、字符串、Set、Map、arguments、NodeList都是原生可遍历的
非原生可遍历:
- 一般的对象
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() 方法:
includes() 方法:
Array.from() 方法:
find() 和 findIndex()方法:
Object.assign() 方法:
Object.keys()、Object.values()、Object.entries()方法:
实例化构造函数生成实例对象:
- 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(() => {}, () => {});
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 => {
});
Promise.resolve():
- Promise.resolve() 是成功状态 Promise() 的一种简写形式
new Promise(reslove => {
reslove('参数');
}).then(data => {});
// 可简写为
Promise.reslove('参数').then(data => {});
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 的表现
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 方法处理
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() {};
实例属性、静态方法和静态属性:
私有属性和方法:
extends:
super:
什么是 Module:
- 一个一个的局部作用域的代码块
什么是 Module 系统:用于解决如下问题
- 模块化问题
- 消除全局变量
- 管理加载顺序
Module 的基本用法:
- 使用 module 前需要搭建服务器系统,在 VSCode 插件中心安装 Live Server
- 只要会用到 import 或 export,在使用 script 标签加载的时候,就要加上 type=”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>
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';
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
// 切换安装源:
npm config registry https://registry.npm.taobao.org
// 安装最新版:
npm install --save-dev @babel/core @babel/cli
// 安装指定版本:
// npm install --save-dev @babel/[email protected] @babel/[email protected]
使用 Babel 编译 ES6 代码:
- Babel 的配置文件
告诉 Babel 将代码转换成什么版本:
npm install @babel/preset-env --save-dev
// npm install @babel/[email protected] --save-dev
需要在项目根目录下创建:babel.config.json(原来是:.babelrc) 文件,然后在文件中添加如下语句
{
"presets": ["@babel/preset-env"]
}
// babel src -d lib 意思是将源目录输出到 lib 目录下,-d 表示输出
// 原写法:babel src --out-dir lib
// src 和 lib 文件目录名可以改,默认使用 src lib
"scripts": {
"build": "babel src -d lib"
},
然后就可以编译项目:
npm run build
Webpack 是什么:
- Webpack 的官网:https://www.webpackjs.com/
- webpack 是静态模块打包器,当 webpack c处理应用程序时,会将所有这些模块打包成一个或多个文件
- 模块:webpack 可以处理 js/css/图片、图标字体等单位
- 静态:开发过程中存在于本地的 js/css/图片/图片字体等文件,就是静态
- webpack 没办法处理动态的内容,只能处理静态的
Webpack 初体验:
- 初始化项目
npm init
// 自动安装最新
npm install --save-dev webpack-cli webpack
// 指定版本
// npm install --save-dev [email protected] [email protected]
"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>
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 [email protected] @babel/[email protected] @babel/[email protected]
引入 core-js 编译新增 API
安装好 core-js 后必须在需要使用的源文件中导入:import “core-js/stable”;
npm install --save-dev core-js
// 指定版本
// npm install --save-dev [email protected]
添加 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']
})
]
};
处理 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']
}
]
},
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 处理图片: