目录
Promise
1. 作用是解决异步回调的问题,传统回调问题是通过函数的回调函数完成的,这样就会一直嵌套回调函数
// 例:
let a = 10;
let promise = new Promise(function(resolve, reject) {
if(a=10) {
resolve("成功"); // 启动promise之后,成功状态下返回resolve的内容
}else{
reject("失败"); // 启动promise之后,失败状态下返回reject的内容
}
});
// promise.then(success, fail); then会回调promise的状态一般then的第一个参数表示调用成功状态,第二个表示调用失败状态。
promise.then (res => {
console.log(res);
}, err => {
console.log(err);
});
// PS:捕获错误的第二种方法promise.catch(err=>{...}),也可以连起来使用,因为then()返回的也是promise对象。promise.then(res=>{...}).catch(err=>{...})
2. promise.resolve()和promise.reject() 可以单独分开使用,相当于创建promise对象时只创建其中某一种状态。
promise.resolve("aa"):将括号中的参数转成一个promise对象, resolve(成功)状态
等价于
new Promise(resolve => {
resolve(aaa")
});
promise.reject("aa"):将括号中的参数转成一个 promise对象, reject(失败)状态。
等价于
new Promise((resolve, reject)=>{
reject(aaa")
});
3. Promise.all([对象1,对象2....]) 批量处理promise对象,将对象打包成一个数组再处理,但必须保证所有的对象都是resolve(成功)状态。
// 例:
let p1 = new Promise(function(resolve, reject) {
resolve('aaa');
})
let p2 = new Promise(function(resolve, reject) {
resolve('bbb');
})
let p3 = new Promise(function(resolve, reject){
resolve('ccc');
})
Promise.all([p1, p2, p3]).then(res => {
console.log(res);
})
4. Promise.race([对象1,对象2....]) 批量处理promise对象,将对象打包成一个数组再处理,异步中只要有一个先解决了就返回解决的那个,不论是resolve还是reject。
// 例:
let p1 = new Promise(function(resolve, reject){
setTimeout(() => {
resolve('aaa')
}, 1000);
})
let p2 = new Promise(function(resolve, reject){
setTimeout(() => {
resolve('bbb')
}, 100);
})
let p1 = new Promise(function(resolve, reject){
setTimeout(() => {
reject('ccc')
}, 500);
})
Promise.race([p1, p2, p3]).then(res => {
console.log(res); //返回结果ccc
})
模块化
1. ES6之前主要用的规范:
commonjs 主要服务端 nodeJs require('http')
AMD 客户端 requireJs, curlJs
CMD 客户端 sealJs
ES6统一了服务端和客户端的模块化规范
2. 定义模块化 export 要定义的内容 或 export { }
// 例1:
export const a = 12;
// 例2:
const a = 5;
let b = 23;
export {a, b}
export {a as first, b as second} //as 别名
3. 如何使用模块化 import
<script type=”module”>
import .... 或 import {..} from ... 或import * as 别名 from ...
</script>
// 例1:
<script type='module'>
import './user.js'; //引入路径
</script>
// 例2:
<script type='module'>
import {a, b, c} from './user.js'; //引入文件中的变量值
</script>
// 例3:
<script type='module'>
import * as all from './user.js'; //引入文件中所有的变量值
</script>
PS: ①import引入的路径可以是相对路径,也可以是绝对路径
②import只会引入一次,且import “./user.js”这种用法只是单纯的引入文件。
③import引用会提升到最前面,所以写在代码哪里都行。
④import引入的js文件,如果发生修改,Import引用时也会相应修改,不会因为只引用就不会修改。
4. export和import的联系
export定义的变量(值) ,如果是export default a=12 则在import时,不需要加”{}”,直接写import a from './user.js',如果是export { ... }或者不是default的状态,import时就必须要加”{}”,即import {a,b,c} from './user.js'。
5. 动态引入 import(路径)
import(路径)类似node里面require,可以动态引入。解决了引入只能静态先引后用,无法进行先判断后引用的问题。可以按需加载。返回值是个 promise对象。
// 例:
function config(count) {
if (count === 1) {
return "./user.js";
} else {
return "./info.js";
}
}
import(config(2)).then(res => {
console.log(...); // 只有当count=2时才加载
});
类和继承
1. 传统的类(面向对象):
构造函数:
function Person(name, age) {
this.name = name;
this.age = age;
}
原型:
Person.prototype.show = function() {
return `个人信息:名字${this.name},---年龄${this.age}`;
}
New一个对象:
var person1 = new Person("jack", "18");
console.log(person1.show());
2. ES6中的类(关键字 class constructor)
class Person {
constructor(name, age) { //构造函数,new一个对象时自动执行
this.name = name;
this.age = age;
}
showName() { //方法
return `我的名字:${this.name}`;
}
showAge() { //方法
return `我的年龄:${this.name}`;
}
}
let person1 = new Person("jack", "18");
console.log(person1.showName());
// PS:也可以用一个变量代替类,如const Person = class {.....}
3. 注意点1: 类外变量引入
let showAge = "userinfo";
class Person {
constructor(name) { // 构造函数,new一个对象时自动执行
this.name = name;
this.age = age;
}
showName() { // 方法
return `我的名字:${this.name}`;
}
[showAge]() {
return `我的年龄:${this.name}`;
}
}
let person1 = new Person("jack", 18);
console.log(person1.showName()); // 我的名字:jack
console.log(person1.userinfo()); // 我的年龄:18
// PS:这里将类外面的变量showName传入方法名的方括号中,作为一个属性,其实[showName]就等于userinfo,所以在调用时可以用person1.userinfo()。
4. 注意点2: 变量提升
let person1 = new Person("jack");
console.log(person1.showName()); // 结果是undefined
class Person {
constructor(name) { // 构造函数,new一个对象时自动执行
this.name = name;
}
showName() { // 方法
return `我的名字:${this.name}`;
}
}
// PS:传统类存在变量提升的情况,可以先使用后声明,class类要先声明再调用,不存在变量上升的情况。
5. 注意点3: this的问题
有时候this会指向运行环境的window,因此想强行制定this为实例的对象,可以采用如下方法:
- 在constructor中显示绑定:this.showName=this.showName.bind(this);
- 使用箭头函数,因为箭头函数没有this,函数里的this只会继承最近的作用域。也就是Person实例。
class Person {
constructor(name) { // 构造函数,new一个对象时自动执行
this.name = name;
}
showName() { // 方法
return `我的名字:${this.name}`;
}
}
let person1 = new Person("jack");
let show = person1.showName;
show( ) // 结果会报错,找不到name,因为this指向window
6. 注意点4: class也存在setter(设置值)和getter(取值)
class Person {
constructor(name) { //构造函数,new一个对象时自动执行
this.name = name;
}
set Name(val) {
return `我的名字:${val}`;
}
get Name() {
return `我的名字:${this.name}`;
}
}
let person1 = new Person("jack");
person1.Name="jack"; // 触发set
console.log(person1.Name); // 触发get
7. 注意点5: 静态方法(static) // 调用时不是对象实例调用,而是类本身调用。
class Person {
constructor(name) { // 构造函数,new一个对象时自动执行
this.name = name;
}
showName() {
return `我的名字:${this.name}`;
}
static info() {
return "这是静态方法";
}
}
let p1 = new Person("jack");
p1.showName(); // 正常状态调用方法
Person.info(); // 调用静态方法
8. 类的继承
原生js的继承方式:用call和apply继承属性,用prototype继承方法。
// 例:
// 父类
function Person(name) {
this.name = name;
}
Person.prototype.showName = function() {
return this.name;
}
// 子类
function Student(name,age) {
Person.call(this, name); // 继承属性
this.age = age;
}
Student.prototype = new Person(); // 继承方法
let st = new Student("jack", 18);
console.log(st.showName());
ES6中的继承:关键字(extends,super)
// 例:
// 父类
class Person {
constructor(name) { // 构造函数,new一个对象时自动执行
this.name = name;
}
showName() {
return `我的名字:${this.name}`;
}
}
// 子类
class Student extends Person {
constructor(name,age){ // 这里的name是继承自父类的,age是子类单独的。
super(name); // 继承父类的内容
this.age = age; // 自己的内容
}
showName() {
//子类的方法如果和父类重名就会覆盖父类的方法,但是用super.就会防止方法覆盖
super.showName(); // 继承自父类的方法
console.log("这是子类的方法"); // 子类自己的方法
}
showAge() { // 子类的方法
return `我的年龄:${this.name}`;
}
}
let st = new Student("jack",18);
console.log(st.showName());
Symbol & Generator
1. Symbol是一个数据类型,类似于Number,Boolean
定义:let syb = Symbol("aaa");
// PS: Symbol数据类型不能像其它数据类型一样new
2. Symbol()返回一个唯一的值。
一般用于key使用,定义一些唯一的或者私有的东西。
3. symbol是基本数据类型。
和string,object,function,undefined,number,boolean一样
4. symbol作为key使用时,for..in..循环无法打印出结果。
5. generator函数解决异步,深入嵌套的问题。
// 定义:
function * gen() { // 需要加上*
yield 'welcome';
yield 'to';
return '牧码人';
}
手动单步调用:
let g1= gen(); // 创建generator对象
g1.next(); // {value:”welcome”, done:false}
g1.next(); // {value:”to”, done:false}
g1.next(); // {value:”牧码人”, done:true}
循环多步调用:for...of..., for..of..循环只能遍历出yield的内容,无法调用return的内容。
for(let val of g1) {
console.log(val); // welcome to 无法打印出牧码人
}
6. generator可以配合解构运算符,Array.from()来使用
7. generator配合axios使用
// 例:
function * gen() {
let val = yield "jack"
yield axios.get(`https://api.github.com/users/${val}`);
}
let g1= gen();
let username= g1.next().value;
g1.next(username).value.then(res => {
console.log(res.data);
});
Async & await
1. Async的写法
async function fn( ) { // 表示异步,这个函数里面有异步任务
(let result = ) await xxx // 表示后面结果需要等待
}
2. Async特点:
(1). await只能放到 async函数中,配合使用。
(2).相比 genrator语义化更强。
(3). await后面可以是 promise对象,也可以数字、字符串、布尔值。
(4). async函数返回是一个 promise对象。
(5).只要 await语句后面 Promise状态变成 reject,那么整个 async函数会中断执行。
3. 如何解决 async函数中抛出错误,影响后续代码执行的问题
try{}catch(e){}
try {
await Promise.reject("出错了");
} catch(e) {
throw new Error()
}
await Promise.resolve("成功");
Promise本身的catch
await Promise.reject("出错了").catch(err => {
console.log(err);
});
4. 简单例子:
async function fn() {
try{
await Promise.resolve("seccess");
await Promise.reject("error");
}catch (e) {
console.log(err);
}
}
fn().then(res => {
console.log(res);
}, err => {
console.log(err);
});
Set和weakSet
1. 定义
set是一种数据结构,类似数组,但是里面不能有重复值。格式:let setArr = new set(["a","b"]); set中放的是一个数组
// 例:
let setArr = new Set('a','b','c','d'); //打印时显示{'a','b','c','d'}。
let setArr = new Set('a','b','c','b'); //打印时显示{'a','b','c'}, 因为b重复就会去掉。
2. Set的数据操作
添加: add // 返回set对象
// 例:
let setArr = new Set(["a"]);
setArr.add("b").add("c").add("d");
console.log(setArr); // {'a','b','c','d'}
// 例:
let setArr = new Set('a','b','c','d');
setArr.delete("a");
console.log(setArr); // {'b','c','d'}
// 例:
let setArr = new Set('a','b','c','d');
setArr.has("a") // true
setArr.has("e") // false
// 例:
let setArr = new Set(['a','b','c','d']);
setArr.clear();
console.log(setArr); // {}
查询Set数据类型的长度:size =>区别于上边的方法,这个是属性.
// 例:
let setArr = new Set(['a','b','c','d']);
console.log(setArr.size); // 4
let setArr = new Set('a','b','c','d');
console.log(setArr.size); // 1
3. Set的循环操作
Set循环时只能用到for(let item of ...), for in和其他的循环模式都不适用。
// 例:
var setArr = new Set(['a','b','c','d']);
for(let item of setArr) {
console.log(item);
}
用forEach()循环
// 例:
var setArr = new Set(['a','b','c','d']);
setArr.forEach((key,value) => {
console.log(key, value);
})
4. Set也支持keys()、values()、entries()方法
// 例:
let setarr = new Set(['a','b','c','d']);
for(let item of setarr.keys()) { // 键
console.log(item);
}
for(let item of setarr.values()) { // 值
console.log(item);
}
for(let item of setarr.entries()) { // 某一项
console.log(item);
}
5. 利用set实现数组去重
// 例:
let arr=[1, 2, 3, 4, 5, 6, 7, 3, 4, 7, 2, 1];
let newArr=[...new Set(arr)];
console.log(newArr); // [1, 2, 3, 4, 5, 6, 7]
6. Set和WeakSet的区别(一般不推荐用WeakSet)
Set和WeakSet在用法上没有区别,但是WeakSet 的成员只能是对象,而不能是其他类型的值。WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
Map和WeakMap
1. 定义
Map是一种数据结构,类似json,但是json的键(key)只能是字符串,map的key可以是任意类型。
例:
let map = new Map();
2. Map的数据操作:
设置值(set)和读取值(get) map.set(key,value); map.get(key);
// 例:
let map = new Map();
let json = {
a: 1,
b: 2
}
map.set('a', 'aaa');
map.set('a', json);
map.set(json, 'aaa');
map.get(json); // 'aaa'
删除: delete()
// 例:
let map = new Map();
map.set('a', 'aaa');
map.set('b','bbb');
map.delete('b');
清空: clear()
// 例:
let map = new Map();
map.set('a', 'aaa');
map.set('b','bbb');
map.clear(); //{ 0 }
查找: has()
例:
let map = new Map();
map.set('a', 'aaa');
map.set('b','bbb');
map.has('a'); // true
3. Map的循环操作
Map和Set的循环一样,可参照Set的循环。
4. WeakMap和Map的区别
Weakmap的key只能是对象,而Map的key可以是任意类型值
数字变化和Math新增的内容
1.Math.pow(底数,指数) // 平方
Math.pow(底数,指数) = 底数**指数
// 例:
Math.pow(2, 3) = 2**3 = 8
2. 进制的写法:
- 二进制(Binary): let a= 0b010101;
- 八进制(Octal): let a= 0o666;
- 十六进制: #ccc
3. Number.isFinite() -->判断是不是数字
// 例:
let a = 10;
let b = '你好'
Number.isFinite(a) // true
Number.isFinite(b) // false
4. Number.isInteger() --->判断数字是不是整数
// 例:
let a = 10;
let b = 10.5;
Number.isFinite(a) // true
Number.isFinite(b) // false
5. 之前学的Number()、 parseInt()、 parseFloat()也可以用于判断数字和数字转换。这里就不再细说了。
6. 安全整数
范围: -(2^53 - 1) <= a <= (2^53 - 1)
检验:Number.isSafeInteger(a);
Number.MAX_SAFE_INTEGER 最大安全整数 ==(2^53 - 1)
Number.MIN_SAFE_INTEGER 最小安全整数= -(2^53 - 1)
7. 其他零散知识:
- Math.trunc()截取,只保留整数部分
Math.trunc(4.5) -> 4
Math.trunc(4.9) -> 4
- Math.sign(5) 判断一个数到底是正数、负数、0
Math.sign (-5) -> -1
Math.sign(5) -> 1
Math.sign(0) -> 0
Math.sign(-0) -> -0
其他值返回NaN
- 开方
二次方: Math.sqrt(要开方数)
三次方: Math.cbrt(要开方数)
ES9中新增的内容
1. 命名捕获: ?<命名>
// 例:
let str = '2018-03-20'
let reg =/(?<year>\d{4})-(?<month>\d{2}-(?<day>\d{2})/;
let {year, month, day} =str.match(reg).groups; // 存放在groups中
console.log(year, month, day);
2. 反向引用 (传统的和新增的可同时用)
传统:\1 \2 replace中用 $1 $2
// 例:
let reg = /^(?<Strive>welcome)--\1$/
let str4='welcome--welcome';
console.log(reg.test(str4)); // true
// 例:
let str = '2018-03-20'
let reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
str=str.replace(reg,'$1/$2/$3 ');
console.log(str); // 2018/03/20
新增:反向引用命名捕获--> 语法:\k<名字> replace中用$<命名>
// 例:
let reg = /^(?<Strive>welcome)-\k<Strive>$/
let str = 'a-a';
let str2 = 'Strive-Strive';
let str3 = 'welcome-welcome';
console.log(reg.test(str)); // false
console.log (reg.test(str2)); // false
console.log(reg.test(str3)); // true
// 例:
let str='2018-03-20'
let reg =/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
str=str.replace(reg,'$<year>/$<month>/$<day> ');
console.log(str); // 2018/03/20
3. dotAll模式 ( s )
之前’.’在正则里表示匹配任意东西,但是不包括\n、表情符号等,加上s,’.’就能真正匹配所有。
let reg=/^w+/gims
4. 标签函数 ( `` )
例:
function fn(arg) {
console.log(arg); // ["welcome"]
}
fn`welcome`;
Proxy的使用
1. 理解Proxy
Proxy用于修改某些操作的默认行为,用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。等同与在语言层面做出修改,即对编程语言进行编程。Proxy 可以理解成,在目标对象之前架设一层”拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来”代理”某些操作,可以译为”代理器”。
2. 语法:
let p = new Proxy(target, handler);
target:用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler是一个对象,其属性是当执行一个操作时定义代理的行为的函数。
{
set(target,property,value){}, //触发设置对象的时候要干的事情
get(target,property){}, //触发获取对象信息的时候要干的事情
deleteProperty(target,property){}, //触发删除对象信息的时候要干的事情
has(target,property){}, //触发查询对象信息的时候要干的事情 'xxx' in obj
apply(target,context,args)∥ 调用函数处理,拦截函数 context是this指向。
....
}
3. apply需要和Reflect配合使用
如果在proxy中单独使用apply,则在使用时只会拦截函数,不会执行其他内容,因此要想执行代理的函数就需要reflect.apply()反射配合使用。
语法:Reflect.apply(调用的函数,this指向,参数数组),类似于call和apply。
// 例:
function sum(a,b) {
return a+b;
}
let newSum = new Proxy(sum, {
apply(target,cot,args) {
console.log("sum函数被拦截了");
return Reflect.apply(...arguments);
}
});
console.log(newSum(2,3)); // 5
Reflect的使用
1. reflect介绍
Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。ES6 中将 Object 的一些明显属于语言内部的方法移植到了 Reflect 对象上(当前某些方法会同时存在于 Object 和 Reflect 对象上),未来的新方法会只部署在 Reflect 对象上。Reflect 对象对某些方法的返回结果进行了修改,使其更合理。Reflect 对象使用函数的方式实现了 Object 的命令式操作。
2. Reflect的13种方法
- Reflect.apply(target, thisArg, args) //等同于Function.prototype. apply. call(func, thisArgs, args),用于绑定this对象后执行给定函数。
- Reflect.construct(target, args) //等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。
- Reflect.get(target, name, receiver) //查找并返回target的name属性,如果没有,则返回undefined。如果name属性部署了读取函数(getter),则读取函数的this绑定的receiver。如果第一个参数不是对象,则Reflect.get则会报错。
- Reflect.set(target, name, value, receiver) //设置target对象的name属性等于value。如果name属性设置的赋值函数,则赋值函数的this绑定receiver。如果Proxy与Reflect联合使用,前者完成拦截赋值操作,后者完成赋值默认行为,而且传入了receiver,则Reflect.set会触发Proxy.defineProperty拦截。
- Reflect.defineProperty(target, name, desc) //等同于Object.define Property ,用来为对象定义属性。未来后者会被逐渐废除。如果第一个参数不是对象,就会抛出错误信息。
- Reflect.deleteProperty(target, name) //等同于delete obj[name],用于删除对象属性。
- Reflect.has(target, name) //对应 name in obj 里面的in操作,如果第一个参数不是对象,Reflect.has和in都会报错。
- Reflect.ownKeys(target) //用于返回对象的所有属性,等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和。
- Reflect.isExtensible(target) //等同于Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。
- Reflect.preventExtensions(target) //等同于Object.preventExtensions,用于让一个对象变为不可扩展,返回一个布尔值,表示是否操作成功。
- Reflect.getOwnPropertyDescriptor(target, name) //等同于Object.getOwn PropertyDescriptor,用于得到指定属性的描述对象,将来会代替后者。
- Reflect.getPrototypeOf(target) //用读取对象的__proto__属性,对应Object.getPrototypeOf(obj)方法。
- Reflect.setPrototypeOf(target, prototype) //设置对象的__proto__属性,返回第一个参数对象,对应Object.setPrototypeOf(obj, newProto,如果第一个参数不是对象,Object.setPrototypeOf会返回第一个参数对象,而Reflect.setPrototypeOf会报错。如果第一个参数是undefined或null,则两个都会报错。
扩展知识:
异步:不连续,上一个操作没有执行完,下一个操作照样开始
同步:连续执行,上一个操作没有执行完,下一个没法开始
关于异步,解决方案:
a).回调函数
b).事件监听
c).发布/订阅
d).Promise对象