ES6
ES6 是 JavaScript 语言的标准 , 可以编写些复杂的大型项目 (企业级)
教程网站 : 点击跳转 (opens new window)
# 变量
# 作用域
如果变量定义在 花括号{}
内 , 那么该 变量的作用范围也就仅限于 花括号{}
内 (let定义)
if(true){
let i = 10;
}
// 无法打印
console.log(i);
如果变量采用
var
导致为全局变量
var与let区别
var | let | |
---|---|---|
重复声明 | 允许(旧覆盖新) | 不允许 |
作用范围 | 全局范围 | 仅限花括号内 |
声明提前(声明前调用) | 允许(但不会显示数据) | 不允许 |
# 空值运算符
空值合并运算符 ??
(和 ||
, &&
用法一样) , 当左侧变量为 null
/undefined
时 , 返回右侧变量 , 否则返回左侧
示例 : (可通过以下代码区分 ||
和 ??
区别)
console.log(undefined || "Sans") // Sans
console.log(undefined ?? "Sans") // Sans
console.log(NaN || "Sans") // Sans
console.log(NaN ?? "Sans") // NaN
console.log(null || "Sans") // Sans
console.log(null ?? "Sans") // Sans
console.log(0 || "Sans") // Sans
console.log(0 ?? "Sans") // 0
# 逻辑赋值操作符
逻辑赋值操作符 ??=
, &&=
, ||=
, 可在判断的时候进行赋值
let a = true;
let b = false;
a ||= b // a = a || b; (true)
a &&= b // a = a && b; (false)
let obj = {name:'Sans'}
obj.age ??= 22
consoel.log(obj.age) // 22
# 字符串拓展
# 字符串方法
对象方法示例 : 点击跳转 (opens new window)
方法 | 说明 |
---|---|
str.padStart(number, string) | 在开头填充指定字符直到number的长度 |
str.padEnd(number, string) | 在末尾填充指定字符直到number的长度 |
str.trimStart() | 清除开头的所有空格符 |
str.trimEnd() | 清除结尾的所有空白符 |
str.replace(reg , targetStr) | 按正则替换为targetStr |
str.replaceAll(replaceStr , targetStr) | 将所有replaceStr替换为targetStr |
# 模板字符串
以往在拼接 H5标签时 , 是通过 ""
/'
引号的 , 如果添加了换行和变量 , 那么会显得很难阅读 .
模板字符串正式解决了这一痛点
let name = 'Sans';
// 引号拼接
let node = "<span>\
<b>" + name + "</b>\
</span>";
// 模板字符串 (支持表达式)
let node2 = `<span>
<b>${name}</b>
</span>`;
let node3 = `<span>
<b>${name===''?'名称不存在':name}</b>
</span>`;
// 原型 <span><b>Sans</b></span>
# 数组拓展
拓展运算符 用法展示 :
let arr = [1,2,3];
// 复制
let arr2 = [...arr];
// 提取
let {a,..b} = arr;
console.log(a,b) // 1 [2,3]
Array.from()
arguments
可以在形参中获取没有实参中的内容 , 但获取到的数据并非是数组形式 , 需要借助 ==Array.from()== 能够将 类数组结构 转换为 数组
function test(){
console.log(Array.from(arguments))
}
test(1,2,3,4)
Array.flat()
数组扁平化 . 进行将递归层级的数组进行降维
// 案例1
let arr = [
['北京','上海','广州'],
['南京','厦门','南宁']
]
console.log(arr.flat()); // ['北京','上海','广州','南京','厦门','南宁']
// 案例2
let arr2 = [{
name: "A",
list: ['北京','上海','广州']
},{
name: "B",
list: ['南京','厦门','南宁']
}];
let res = arr2.flatMap(item => {
return item.list;
})
console.log(res); // ['北京','上海','广州','南京','厦门','南宁']
at()
拿取指定索引数据 , 负数则为倒数元素进行索引 , 字符串也可以行
let arr = ['no1','no2','no3','no4']
console.log(arr.at(0)) // arr[0] (no1)
console.log(arr.at(-1)) // arr[arr.length-1] (no4)
# 数组复制
以下数组复制 , 只是复制了指针 , 并非开辟内存空间
const a1 = [1, 2];
const a2 = a1;
a2[0] = 2;
a1 // [2, 2]
以下代码 , 复制形式 开辟了内存空间
const a1 = [1, 2];
const a2 = a1.concat();
const a3 = [...a1]
a2[0] = 2;
a1 // [1, 2]
# 正则拓展
# 正则方法
方法 | 说明 |
---|---|
str.match() | 获取 正则匹配值 的数组 (全局匹配末尾加g) |
str.matchAll(reg) | 获取 正则匹配值 的数组 |
str.replace() | 获取 正则匹配值替换预期值 的新字符串 |
str.search() | 获取 正则匹配的首个索引 , 不存在 -1 |
str.test() | 是否有 正则匹配值 |
str.exec(str) | 获取 正则匹配值 的数组 (详细 |
// ES6 新特性 exec()
let str = "今天是2022-11-11";
// exec()
console.log(/[0-9]{4}-[0-9]{2}-[0-9]{2}/.exec(str)) // ['2022-11-11', index: 3, input: '今天是2022-11-11', groups: undefined]
console.log(/([0-9]{4})-([0-9]{2})-([0-9]{2})/.exec(str)) // ['2022-11-11', '2022', '11', '11', index: 3, input: '今天是2022-11-11', groups: undefined]
console.log(/(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/.exec(str)) // ['2022-11-11', '2022', '11', '11', index: 3, input: '今天是2022-11-11', groups: {day: '11',month: '11',year: '2022'}]
// ES6 新特性 matchAll()
let str = `
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
`;
let reg = /<li>(?<content>.*)<\/li>/g;
for(let item of str.matchAll(reg)){
console.log(item.groups.content)
}
/*
111
222
333
444
*/
# 对象拓展
# 对象简写
const foo = 'bar';
const baz = {
foo,
name: 'Sans',
test(){...}
};
# 属性名表达式
可以通过变量进行定义变量名
let name = 'sans'
let obj = {
[name]: 'zs'
}
console.log(obj); // {sans: 'zs'}
# 拓展运算符
可以跟数组一样 使用 ...
进行操作
let obj1 = {name: 'zs'};
let obj2 = {age: 12};
console.log({...obj1, ...obj2});
// 等同于
// {}空对象 是将 obj1和obj2分别合并到新的空对象
console.log(Object.assign({}, obj1, obj2));
# 可选链操作符
可选链操作符 ?
, 作用是 判断操作符前面的变量是否为 null
/undefined
, 如果是 则不会进行往后的链式调用
let obj1 = {
name: 'sans',
location: {
city:'广东'
}
}
let obj2 = {
name: 'sans'
}
console.log(obj1.location.city) // 广东
console.log(obj2.location.city) // 报红
console.log(obj2?.location?.city ?? '这人很懒,啥也没写') // undefined
意图 : 尽可能避免没有意义的报红警告
# 对象方法
对象方法示例 : 点击跳转 (opens new window)
方法 | 说明 |
---|---|
Object.assign(targetObj, ...sourceObj) | 克隆对象 ( 可拷贝多个对象至目标对象) |
Object.is(obj1, obj2) | 比较对象 (比 == , === 更为严谨) |
Object.keys(obj) | 获取 对象所有属性名 的数组 |
Object.values(obj) | 获取 对象所有属性值 的数组 |
Object.entries(obj) | 获取 对象所有 属性名和属性值 的二维数组 |
Object.getOwnPropertyDescriptors() | 获取 获取所有属性描述信息 |
Object.fromEntries(arr) | 将 键值对的二维数组/Map对象 转为对象 (逆操作) |
Object.fromEntries()
// 用法1
let arr = [['name','zs'],['age',22]];
console.log(Object.fromEn tries(arr))
// 用法2
let map = Map();
map.set("name","zs");
map.set("age",22);
console.log(Object.fromEntries(map))
// 用法3
let str = "name=zs&age=22"
let seatchParams = new URLSearchParams(str);
console.log(Object.fromEntries(seatchParams))
// 用法4
let obj = {
"广东": ["广州","佛山","深圳"],
"广西": ["南宁","贵港"],
"江苏": ["南京"]
}
let myarr = Object.entries(obj)
let mynewarr = myarr.map(([key,value])=>{
[]
})
console.log(Object.fromEntries(myarr))
# 私有特征
ES13更新中的新功能 , 通过在 变量/方法 前面添加 #
代表私有 , 不过访问是需要调用内部方法进行访问
class people{
#obj = {}
get(key){
return this.#obj[key]
}
set(key, value){
this.#obj[key] = value
}
}
let store = new people();
store.set("name","Sans");
store.set("age",22);
// console.log(store.#obj) 报红
console.log(store)
# 函数拓展
# 装配参数
函数可以定义可选默认值
// url必选 , 其余可选
function ajax(url, method="get", async=true){....}
ajax("/test");
ajax("/test", 'post');
ajax("/test", 'post', false);
# 剩余参数
多个变量的时候可以采用 ...
function test(...arr){
console.log(arr); //[1,2,3,4]
}
test(1,2,3,4);
let obj {
code: 200,
data: "zs"
}
let {name, ...others} = obj
应用场景 ()
function ajax(options){
const defaultOptions = {
methods: 'get',
async: true
}
options = {...defaultOptions, ...options}
...
}
ajax({
url: '...'
})
# 函数Name
函数变量可以通过 name
属性 进行获取 函数的名称
function test(){....}
console.log(test.name)
# 箭头函数
简写函数的作用
let test = function(e){...}
let test = (e)=>{...}
简写情况 :
- 参数只有一个的情况 ; 省略 方法括号
()
- 函数体只有一条语句的情况 ; 省略返回
return
注意 :
- 函数体只有一条语句的情况且是返回一个对象 , 必须在对象外的
{}
花括号 加上()
括号 - 箭头函数没有this , this是访问父作用域 (箭头函数应用严紧)
# Symbol
Symbol
是一种新的 原始数据类型 , 表示独一无二的值 !
// 实例 Symbol类型数据
let s = Symbol();
console.log(typeof s)
let s2 = Symbol();
console.log(s === s2) // false
Symbol实例中可添加参数 作为标识 (方便理解) ==let s = Symbol('name')==
这一特性解决了 属性名的冲突 问题 , 示例 :
let obj = { name : 'sans' }
let name = Symbol('name');
obj[name] = 'zs';
console.log(obj[name]) // zs
console.log(obj.name) // sans
主要意图是 该对象别人使用相同名称时 , 可防止冲突 , 因此 封装者 一般会采用这种方式
规范用法
let keys = {
name: Symbol('name'),
age: Symbol('age'),
test: Symbol('test')
}
let obj = {
[keys.name]: 'Sans',
[keys.age]: 22,
[keys.test]() {
console.log('方法测试');
}
}
console.log(obj);
obj[keys.test]();
# 遍历Symbol
Symbol属性名 不能被 for...in
/for...of
遍历 , 也不会被Object.keys()
/Object.getOwnPropertyNames()
/JSON.stringify()
返回
Object.getOwnPropertySymbols()
获取指定对象的所有Symbol属性名
Reflect.ownKeys()
获取指定对象的所有属性
let obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj.name = 'sans';
obj[a] = 'Hello';
obj[b] = 'World';
const objectSymbols = Object.getOwnPropertySymbols(obj);
const reflectSymbols = Reflect.ownKeys(obj);
console.log(objectSymbols) // [Symbol(a), Symbol(b)]
console.log(reflectSymbols) // ['name', Symbol(a), Symbol(b)]
// 遍历
reflectSymbols.forEach(item =>{
console.log(item,'=>',obj[item])
})
注意 :
- 不能进行运算
- toString()打印显示 实例本身
- 隐式转换boolean
- map不能遍历 Symbol类型
# Iterator
Iterator迭代器 是一种机制 , 为不同数据结构提供了统一的访问机制
let arr = ['zs','ls','ww'];
let i = arr[Symbol.iterator]();
console.log(i.next()); // {value: 'zs', done: false}
console.log(i.next()); // {value: 'ls', done: false}
console.log(i.next()); // {value: 'ww', done: false}
console.log(i.next()); // {value: undefined, done: true}
原生具备 Iterator接口 的数据结构 :
- Array
- Set
- Map
- String (每个字符的遍历)
- arguments对象
- NodeList对象
ES6规定 , Iterator接口默认在数据结构的 Symbol.iterator
属性 (本身是个迭代器生成函数)
数据结构如果具备有 Symbol.iterator属性 那么可认为是 可遍历的
对 Object类型对象 封装 仅限访问
let list = Symbol('list');
let obj = {
code: 200,
name: 'sans',
// 仅限访问list
[list]: ['zs','ls','ww'],
// 迭代器 访问
[Symbol.iterator](){
let index = 0;
return {
// 迭代器包含有 next()
// 采用箭头函数原因 : this越过函数本身 , 引用对象
next: ()=>{
return{
value: this[list][index++],
done: index>=(this[list].length+1)?true:false
}
}
}
}
}
// 迭代器测试
let i = obj[Symbol.iterator]();
console.log(i.next()) // { value: 'zs', done: false }
console.log(i.next()) // { value: 'ls', done: false }
console.log(i.next()) // { value: 'ww', done: false }
console.log(i.next()) // { value: undefined, done: true }
console.log(i.next()) // { value: undefined, done: true }
// for..of遍历
for(item of obj){
console.log(item)
}
# Set
Set类似数组结构 , 但成员的值都是唯一的 , 没有重复的值
let set = new Set([1,2,3,2,4,4,5,5]);
console.log(s); // Set(5) { 1, 2, 3, 4, 5 }
// 转Array
let arr = [...set];
let arr2 = Array.from(set);
Set基本属性和方法
- size : 获取Set总数
- Set.prototype.add(value) : 添加值
- Set.prototype.delete(value) : 删除值
- Set.prototype.has(value) : 判断是否包含有
- Set.prototype.keys() : 获取键名 遍历器
- Set.prototype.values() : 获取值 遍历器
- Set.prototype.entries() : 获取键值对 遍历器
- Set.prototype.forEach() : 遍历每个成员
数组手写去重
let set = [11,2,3,'sans',{name:'sans'},{name:'ww'},{name:'sans'},11,123123,undefined,NaN,NaN]
// 方式1
function uni(arr){
let res = new Set();
return set.filter(item =>{
let id = JSON.stringify(item);
console.log('id',id)
if(res.has(id)){
return false;
}else{
res.add(id);
return true;
}
})
}
console.log(uni(set));
//方式2
function uni2(arr){
let res = new Set();
arr.forEach(item => res.add(item));
return res;
}
console.log(uni2(set));
//方式3
function uni3(arr){
return [...new Set(arr)];
}
console.log(uni3(set));
# Map
Map类似对象结构 , 键值对的集合 , 但 键 可以是各种类型
let map = new Map([
['name','sans'],
['age',22]
])
// 等同于
let map2 = new Map();
map2.set('name','sans');
map2.set('age',22);
console.log(map); // Map(2) { 'name' => 'sans', 'age' => 22 }
console.log(map2); // Map(2) { 'name' => 'sans', 'age' => 22 }
Map基本属性和方法 包含有 Set应用的方法 点击跳转
# Proxy
Proxy代理 主要作用是 为对象设置个拦截 (监听) , 实时获取对象数据/设置对象数据 等操作
let obj = {};
let proxy = new Proxy(obj,{
get(target, key){
console.log('get', target, key);
return target[key];
},
set(target, key, value){
console.log('set', target, key, value)
target[key] = value;
}
})
proxy.name='sans'
console.log(proxy.name)
其他类型的操作
let s = new Set();
let proxy = new Proxy(s,{
get(target, key){
let v = target[key];
// 如果访问的是方法 , 修正this指向
if(v instanceof Function) return v.bind(target);
return v;
},
set(target, key, value){
console.log('set', target, key, value)
}
})
proxy.add('sans')
console.log(proxy) // Set(1) { 'sans' }
ES5 旧版 , 弊端仅限于对象的属性使用
let obj = {};
// 监听 obj对象的name属性
Object.defineProperty(obj, "name", {
get(){
console.log('get')
},
set(){
console.log('set')
}
})
obj.name = 'sans'
console.log(obj.name)
# Promise
Promise是回调的升级版 , 在处理花费较长时间的任务时 , 使用 Promise 可以进行异步处理 , 防止堵塞
学习来源 : 点击跳转 (opens new window)
基本结构 :
// 参数封装有一个执行器函数
let pro = new Promise((resolve, reject)=>{
if(true){
resolve('succee');
}else{
reject('fail')
}
})
pro.then( res =>{
console.log('succee')
}).catch( err =>{
console.log('fail')
}).finally(()=>{
sonsole.log("一定执行的")
})
响应结构说明 :
- Prototype : 原型类型
- PromiseState : pending(等待) / fulfilled(完成) / rejected(拒绝)
- PromiseResult : 响应数据
案例1 : (检测图片有效)
const imageUrl = '';
const imgPromise = (url) => {
return new Promise( (resolve, reject) => {
const img = new Image();
img.src = url;
// 加载成功
img.onload = () => {
resolve(img);
}
img.onerror = () => {
reject(new Error('图片有误'));
}
} );
};
imgPromise(imageUrl)
.then( img => {
console.log('success : ',img)
})
.catch(err => {
console.log('error: ',err)
})
案例2 : (随机数判断)
new Promise((resolve, reject) => {
setTimeout(() => {
let num = Math.floor(Math.random() * 11);//0-10的随机数
if (num >= 5) {
resolve(num);
} else {
reject(num);
}
},1000)
}).then(res => {
console.log("执行了成功时的回调 , 数值为:"+ res);
}).catch(err => {
console.log("执行了失败时的回调 , 数值为:"+ err);
})
# Promise方法
# Promise.all()
all()方法 用于处理多个异步任务 , 所有执行完后才调用then
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('结果1');
}, 1000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('结果2');
}, 2000);
})
//Promise.all([])接收多个异步任务 , 放入数组中
Promise.all([p1, p2]).then(results => {//results接收多个参数 , 所以是数组
console.log(results);//["结果1", "结果2"]
})
# Promise.race()
race()方法 当中的任务谁先完成就执行谁
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('结果1');
}, 1000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('结果2');
}, 2000);
})
//Promise.race([])中接收多个异步任务 , 放入数组中
Promise.race([p1, p2]).then(result => { //p1和p2 公用同一result , 谁先完成接收谁
console.log(result);
})
# Promise手写
学习来源 : 点击跳转 (opens new window)
class Commitment {
// 状态
static PENDING = '待定';
static FULFILLED = '成功';
static REJECTED = '拒绝';
// 构造方法
constructor(func) {
this.status = Commitment.PENDING;
this.result = null;
// 保存函数
this.resolveCallbacks = [];
this.rejectCallbacks = [];
// 传入异常对象进行操作
try {
func(this.resolve.bind(this), this.reject.bind(this));
} catch (err) {
this.reject(err);
}
}
// 失败
resolve(res) {
// 事件后执行
setTimeout(() => {
if (this.status === Commitment.PENDING) {
this.status = Commitment.FULFILLED;
this.result = res;
this.resolveCallbacks.forEach(call => {
call(res)
});
}
});
}
// 成功
reject(res) {
// 事件后执行
setTimeout(() => {
if (this.status === Commitment.PENDING) {
this.status = Commitment.REJECTED;
this.result = res;
this.rejectCallbacks.forEach(call => {
call(res)
});
}
});
}
then(onFULFILLED, onREJECTED) {
return new Commitment((resolve, reject) => {
// 不是函数则传递空函数
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : () => { };
onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : () => { };
// 如果外部也使用了异步 , 很有可能还是 待定状态
if (this.status === Commitment.PENDING) {
this.resolveCallbacks.push(onFULFILLED);
this.rejectCallbacks.push(onREJECTED);
}
if (this.status === Commitment.FULLFILLED) {
// 异步处理
setTimeout(() => {
onFULFILLED(this.result);
})
}
if (this.status === Commitment.REJECTED) {
// 异步处理
setTimeout(() => {
onREJECTED(this.result);
});
}
});
}
}
// 测试实例
console.log('No.1');
let commitment = new Commitment((resolve, reject) => {
console.log('No.2');
setTimeout(() => {
reject('这次一定');
console.log('No.4');
});
});
commitment.then(
res => { console.log(res) },
err => { console.log(err) }
)
console.log('No.3');
测试步骤说明 : (按照No.x步骤说明)
- (待定) new实例
- (待定) 异步操作 , 执行 then() , 由于是待定状态进行 数组保存函数(resolve/reject)
- (成功) 执行 reject() 里面还有一个异步处理(外部先执行了)
- (成功) 执行外部异步 , 最后和 reject() 共同跑完
# Generator
Generator函数 是ES6 的一种异步机制 . 可理解为状态机 (封装有多种状态)
示例 :
function *gen(){
console.log(11)
let res1 = yield
console.log(`11接收到${res1}`)
console.log(22)
yield '33'
console.log(33)
}
let g = gen()
let res1 = g.next()
let res2 = g.next('11')
let res3 = g.next()
console.log(res1,res2,res3)
/**
11
11接收到11
22
33
{ value: undefined, done: false } { value: '33', done: false } { value: undefined, done: true }
*/
注意 :
- 第一个传入参数是无意义
- Generator作为理解 , 目前流行 Promise语法糖
# Class
JavaScript类的概念应用 和Java类似
示例 :
class Person{
// 构造函数
constructor(name, age){
this.name = name
this.age = age
}
say(){
console.log(this.name, this.age)
}
}
let person = new Person('sans',22);
person.say();
注意 :
- 属性名也支持 属性名表达式用法 示例跳转
- 属性和方法可以进行
state
静态修饰 - class可以 使用
extends
进行继承
# Modlule
JavaScript一直以来没有过模块化体系 , ES6为此增强了 , 提供了 Modlule模块化开发
ES6模块化重点解决了以下问题 :
- 异步加载 (节点加载问题)
- 私密不漏 (外部访问问题)
- 重名不怕 (函数重名问题)
- 依赖不乱 (循序调用问题)
Modlule模块化 功能由 export
, import
关键字组成
export
: 规定模块对外应用的接口 (俗称 暴露/导出)import
: 提取其他模块提供的接口 (俗称 引用/导入)
模块引用 (已模块化形式进行加载)
// 异步加载 (节点加载前 , 可直接访问节点)
<script src=".." type="module"></script>
如果其他模块访问也必须写上该属性 ==type="module"==
模块访问
ES6 采用 导入/导出 (引用/暴露) 的形式进行外部引用
// 模块 1 (导出
function A1(){
console.log("访问A1");
}
export default A1
// 模块 2 (导入
import A1 from './1.js'
A1();
导入/导出 多个 写法
写法1
function A1(){...}
function A2(){...}
export default { A1, A2 } // 看做一个对象
import obj
写法2
function test(){...}
export function A1(){...}
export function A2(){...}
export default test
import textA,{A1,A2} from './1.js'
写法3 (常用)
export function A1(){}
export function A2(){}
export function A3(){}
// 模块2 按需导入
import {A1} from './1.js'
// 模块2 所有导入
import {* as mod1} from './1.js'
注意 :
- 模块导入可能有多个 函数/属性/..
- 模块不导出就没法进行通信
- 多模块 导入所引用的名称不能重复发名称
- 多模块 假如出现重名问题 可以进行 进行重新命名 重名写法 : ==import test as testA1==
- 模块导入时 , 导入所有引用 可以直接用
*
替代 但必须重新命名- 隔离性好 , 那个导入那个用 , 跨出范围不能用 (模块范围)
- 多模块 引用时他们名称必须一致 , 否则不行
- 模块导出时 采用了
default
关键字 , 在导入时需要 自定义起名 , 且无需写大括号引用
# 动态导入
标准用法是静态的 , 会直接使所有模块进行导入 . ES11 新增了按需导入 模块
定义模块加载 :
async funtion test(){
let res;
if(true){
res = await import("./1.js")
}else{
res = await import("./2.js")
}
console.log(res)
}
获取模块路径
通过 ==import.meta== 进行获取模块的路径
# 异步遍历器
==for await==异步遍历器 , 需要异步生成器使用
function timer(t){
return new Promise(resolve=>{
setTimeout(()=>{
resolve("data-"+t)
}, t)
})
}
// 异步生成器
async function *gen(){
// 请求异步任务
yield timer(1000)
yield timer(2000)
yield timer(3000)
}
async function test(){
let g = gen();
let list = [g.next(), g.next(), g.next()]
for await(let i of list){
console.log("op :>>",Date.now())
console.log(i)
console.log("ed :>>",Date.now())
}
}
# WeakRefs
WeakRef (opens new window) 集合对象允许保留对原对象的弱引用 , 而不会阻止被弱引用对象被 GC 回收
一般情况 采用 Set , Map 存数据时 , 传入的原始数据如果赋值
null
/undefined
/ 丢失 等情况 , 集合中的数据是不会丢失的 , 因此采用 WeakRefs集合对象
WeakRef 延伸的集合对象 : (可点击跳转)
示例 : (Map执行和Set差不多)
let obj = {
name : 'Sans'
}
let s = new WeakSet();
s.add(obj)
obj = null
// 执行后访问 s 为空
DOM节点对象
即使丢失了还会存在
<body>
<button id="like">按钮</button>
</body>
<script type="text/JavaScript"> let like = document.getElementById("like");
let map = new WeakMap();
// 因为不能穿简单类型
map.set(like, {click: 0});
like.onclick = function(){
let buttom = map.get(like);
buttom.click++;
console.log(buttom.click);
}
setTimeout(function() {
document.body.removeChild(like)
}, 3000);
// 即使DOM节点被删除了 , 但在后面map访问时 like依旧存在
</script>
**解决方案 : ** DOM节点对象使用 WeakRef进行封装 , deref()能够提取 原始DOM节点
<body>
<button id="like">按钮</button>
</body>
<script type="text/JavaScript">
let like = new WeakRef(document.getElementById("like"));
let wmap = new WeakMap();
// like.deref() 等同于 document.getElementById("like")
wmap.set(like.deref(), {click: 0});
like.deref().onclick = function(){
let buttom = wmap.get(like.deref());
buttom.click++;
console.log(buttom.click);
}
setTimeout(function() {
document.body.removeChild(like.deref())
}, 2000);
</script>
注意 :
- 只能存储复杂类型 对象 / 函数 / 数组 , 基本类型不能
- 不存在引用计数 +1 (原数据指向丢失集合则丢失)
- 不能使用for循环
- DOM节点对象引用 , 即使body消失了 , 集合是不会消失的 , 除非把该DOM节点对象设为null 除非该对象获取到的是 原始DOM节点!
# 异常捕获
和Java差不多 , 捕获回调的数据只能传递 包含cause
属性的对象
function getData(){
try{
let i = 1/0; // 异常
}catch{
throw new Error("说明不符合规则",{cause:'有问题'})
}
}
try{
getData()
}catch(err){
console.log(err, err.cause)
}
← JavaScript Git→