2025-03-29 14:35:49 +08:00

669 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ES6
参考: 阮一峰es6 https://wangdoc.com/es6/
## let const
推荐使用let const 关键字替代 var关键字声明变量因为 var存在诸多问题
* 越域
```js
{
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
```
* var 可以声明多次
* var 会变量提升
## 解构
> 把复合数据类型(对象居多, 数组较少)快速解析成单个变量而无需使用arr[x]或者obj[key]等传统方式进行赋值
### 数组解构赋值
```js
var arr = ['this is a string', 2, 3];
//传统方式
var a = arr[0],
b = arr[1],
c = arr[2];
//解构赋值,是不是简洁很多?
var [a, b, c] = arr;
console.log(a);//this is a string
console.log(b);//2
console.log(c);//3
// 如果想跳过某个元素, 只需多写一个逗号
```
### 嵌套数组解构
```js
var arr = [[1, 2, [3, 4]], 5, 6];
var [[d, e, [f, g]], h, i] = arr;
console.log(d);//1
console.log(f);//3
console.log(i);//6
```
### 函数传参解构
```js
var arr = ['this is a string', 2, 3];
function fn1([a, b, c]) {
console.log(a);
console.log(b);
console.log(c);
}
fn1(arr);
//this is a string
//2
//3
```
### for循环解构
```js
var arr = [[11, 12], [21, 22], [31, 32]];
for (let [a, b] of arr) {
console.log(a);
console.log(b);
}
//11
//12
//21
//22
//31
//32
```
### 对象赋值解构
对象的解构必须要用属性名, 不是根据顺序, 而是属性名对应
```js
var obj = {
name: 'chris',
sex: 'male',
age: 26,
son: {
sonname: '大熊',
sonsex: 'male',
sonage: 1
}
};
var {name, sex, age, son} = obj;
console.log(name + ' ' + sex + ' ' + age); //chris male 26
console.log(son); // { sonname: '大熊', sonsex: 'male', sonage: 1 }
```
### 对象传参解构
```js
var obj = {
name: 'chris',
sex: 'male',
age: 26,
son: {
sonname: '大熊',
sonsex: 'male',
sonage: 1
}
};
function fn2({sex, age, name}) {
console.log(name + ' ' + sex + ' ' + age);
}
fn2(obj);
//chris male 26
```
### 解构重命名变量
```js
var obj = {
name: 'chris',
sex: 'male',
age: 26
};
var {name: nickname, age: howold} = obj;
console.log(nickname + ' ' + howold); //chris 26
```
对象的解构赋值是下面形式的简写
```js
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' }
```
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
```js
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }
baz // "aaa"
foo // error: foo is not defined
```
```js
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
}
let {
loc,
loc: { start },
loc: {
start: { line }
}
} = node
line // 1
loc // Object {start: Object}
start // Object {line: 1, column: 5}
```
上面代码有三次解构赋值,分别是对 loc、start、line 三个属性的解构赋值。注意,最后一次对 line 属性的解构赋值之中,只有 line 是变量loc 和 start 不是变量。
### 嵌套对象解构
```js
var obj = {
name: 'chris',
sex: 'male',
age: 26,
son: {
sonname: '大熊',
sonsex: 'male',
sonage: 1
}
};
var {name, sex, age, son: {sonname, sonsex, sonage}} = obj;
console.log(sonname + ' ' + sonsex + ' ' + sonage);
//大熊 male 1
//Babel暂不支持这种嵌套解构
obj = {
name: 'chris',
sex: 'male',
age: [1, 2, 3]
}
{name, sex, age: [a, b, c]} = obj;
console.log(c);
```
### 对象属性重名
需要更改变量名:
```js
var obj = {
name: 'chris',
sex: 'male',
age: 26,
son: {
name: '大熊',
sex: 'male',
age: 1
}
};
//赋值解构
var {name: fathername, son: {name, sex, age}} = obj;
console.log(fathername); //chris
console.log(name); //大熊
//传参解构
function fn3({sex, age, name, son: {name: sonname}}) {
console.log(name + ' ' + sex + ' ' + age + ' ' + sonname);
}
fn3(obj);
//chris male 26 大熊
```
### 循环解构对象
```js
var arr = [{name: 'chris', age: 26}, {name: 'jack', age: 27}, {name: 'peter',age: 28}];
for (let {age, name} of arr) {
console.log(name + ' ' + age);
}
//chris 26
//jack 27
//peter 28
```
### 定义默认值
> 在解构过程中,如果某个路径上的属性不存在,可以使用默认值。
```js
const user = {
id: 123,
name: 'hehe'
};
const {
education: {
school: {
name
}
} = {
school: {
name: 'NB'
}
}
} = user;
console.log(name); //prints: NB
```
### 解构的特殊应用场景
```js
//变量互换
var x = 1,
y = 2;
var [x, y] = [y, x];
console.log(x); //2
console.log(y); //1
//字符串解构
var str = 'love';
var [a, b, c, d] = str;
console.log(a);//l
console.log(b);//o
console.log(c);//v
console.log(d);//e
```
## 展开运算符
展开运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值
```js
var foo = function(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
}
var arr = [1, 2, 3];
//传统写法
foo(arr[0], arr[1], arr[2]);
//使用展开运算符
foo(...arr);
//1
//2
//3
```
特殊应用场景:
```js
//数组深拷贝
var arr2 = arr;
var arr3 = [...arr];
console.log(arr===arr2); //true, 说明arr和arr2指向同一个数组
console.log(arr===arr3); //false, 说明arr3和arr指向不同数组
//把一个数组插入另一个数组字面量
var arr4 = [...arr, 4, 5, 6];
console.log(arr4);//[1, 2, 3, 4, 5, 6]
//字符串转数组
var str = 'love';
var arr5 = [...str];
console.log(arr5);//[ 'l', 'o', 'v', 'e' ]
```
## 剩余运算符
剩余运算符也是三个点号,不过其功能与展开运算符恰好相反,把逗号隔开的值序列组合成一个数组
```js
//主要用于不定参数所以ES6开始可以不再使用arguments对象
var bar = function(...args) {
for (let el of args) {
console.log(el);
}
}
bar(1, 2, 3, 4);
//1
//2
//3
//4
bar = function(a, ...args) {
console.log(a);
console.log(args);
}
bar(1, 2, 3, 4);
//1
//[ 2, 3, 4 ]
```
剩余运算符配合解构使用:
```js
var [a, ...rest] = [1, 2, 3, 4];
console.log(a);//1
console.log(rest);//[2, 3, 4]
```
## 结构展开运算符小结
等号表达式是典型的赋值形式函数传参和for循环的变量都是特殊形式的赋值。解构的原理是赋值的两边具有相同的解构就可以正确取出数组或对象里面的元素或属性值省略了使用下标逐个赋值的麻烦。
==对于三个点号,三点放在形参或者等号左边为剩余运算符; 放在实参或者等号右边为展开运算符。==
## 结构展开运算符经验
* 在等号赋值或for循环中如果需要从数组或对象中取值尽量使用解构。
* 在自己定义函数的时候,如果调用者传来的是数组或对象,形参尽量使用解构方式,优先使用对象解构,其次是数组解构。代码可读性会很好。
* 在调用第三方函数的时候如果该函数接受多个参数并且你要传入的实参为数组则使用展开运算符。可以避免使用下标形式传入参数。也可以避免很多人习惯的使用apply方法传入数组。
* 剩余运算符使用场景应该稍少一些主要是处理不定数量参数可以避免arguments对象的使用。
## 可选链操作符
> 读取对象内部的某个属性往往需要判断一下属性的上层对象是否存在当其中一链为null或者undefined时就返回undefined这样即使中间缺少一个属性也不会报错
```js
const firstName = message?.body?.user?.firstName || 'default';
```
## 空值合并操作符
> 空值合并操作符(`??`是一个逻辑操作符当左侧的操作数为null或者undefined时返回其右侧操作数否则返回左侧操作数。
​如果使用 `||` 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,`''``0`)时。
```js
const value1 = 0 || 'default'; // 'default'(因为 0 是假值)
const value2 = 0 ?? 'default'; // 0因为 0 不是 null 或 undefined
const value3 = '' || 'default'; // 'default'(因为空字符串是假值)
const value4 = '' ?? 'default'; // ''(因为空字符串不是 null 或 undefined
const value5 = null ?? 'default'; // 'default'(因为 null 是空值)
const value6 = undefined ?? 'default'; // 'default'(因为 undefined 是空值)
```
## 函数的默认值
```js
function show(msg = "...") {
console.log(msg)
}
show()
show('test')
```
* 如果一个函数有多个形参也有默认值: 默认值一定要在后面
```js
function test(a, b, c =1){}
// 不能是 function test(a=1, b, c){}
```
## 箭头函数/lamda表示式
```js
// 两个参数的情况:
let sum = function (a, b) {
return a + b;
}
// 简写为:
//当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。
let sum2 = (a, b) => a + b;
//测试调用
console.log(sum2(10, 10));//20
// 代码不止一行,可以用`{}`括起来
let sum3 = (a, b) => {
c = a + b;
return c;
};
//测试调用
console.log(sum3(10, 20));//30
```
## 模板字符串
```js
`${变量}`
let info = "你好,我的名字是:【"+name+"】,年龄是:【"+age+"】,邮箱是:【】"
console.log(info);
# 模板字符串的写法
let info = `你好,我的名字是:${name},年龄是:${person.age},邮箱是:${person.email}`
console.log(info);
```
## fetch api
另建笔记
## Promise
另建笔记
## async
另建笔记
## Set
> 可以理解为不重复的数组
```js
const set = new Set([1, 5, 3, 4])
set.add(5) // 不能重复
set.delete(3) // 按照内容删除, 没有下标概念, 遍历获取不了index
set.has(4) // true
console.log(set)
```
使用: 数组去重
## Map
> 键值对的集合
Map和Object的区别
* Map 的键可以是任意值,包括函数、对象或任意基本类型
* Object 的键必须是一个 String 或是 Symbol
* Map 的键值对个数可以轻易地通过 size 属性获取
* Object 的键值对个数只能手动计算
* Map 是 可迭代的 的,所以可以直接被迭代
* Object 没有实现迭代协议, 所以不能用 for...of 表达式
* Map 在频繁增删键值对的场景下性能更好
* Map 没有元素的序列化和解析的支持
```js
const map = new Map()
map.set('a', 1) // 添加
map.set('b', 2)
map.has('a') // 判断是否存在
map.get('a') // 获取值
map.size // 获取长度
map.keys() // 获取keys
map.forEach((value,key)=>{console.log(`${key}=${value}`)}) // 遍历
map.delete('a') // 删除
map.clear() // 清空
```
## Symbol
> 表示唯一的不可变的值, 通常用于创建对象的唯一属性键
* 每个 `symbol` 的值都是唯一的, 即使他们的描述相同
* 可以用于对象的键名, 避免冲突
```js
const sym1 = Symbol('key')
const sym2 = Symbol('key')
console.log(sym1 === sym2) // false
const obj = { [sym1]: 1, [sym2]: 2}
```
## BigInt
> 表示任意精度的整数, 可以表示大于 `Number.MAX_SAFE_INTEGER` (9007199254740991) 的整数, 或者小于 `Number.MIN_SAFE_INTEGER` (-9007199254740991) 的负整数
* 在数字后面加 `n` 来创建
* 不能和 `Number` 混合运算
```js
const big = 9007199254740992n
console.log(big)
console.log(big + 1n)
```
## Class
另建笔记
## Proxy
> 创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
```js
const p = new Proxy(target, handler)
```
```js
let target = { name: "Alice", age: 25 };
let handler = {
get(target, prop) {
console.log(`读取属性:${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`设置属性:${prop} = ${value}`);
target[prop] = value;
return true; // 表示设置成功
}
};
let proxy = new Proxy(target, handler);
proxy.name; // 读取属性name
proxy.age = 30; // 设置属性age = 30
```
## for...of
> 执行一个循环,该循环处理来自可迭代对象的值序列。可迭代对象包括内置对象的实例,例如 Array、String、TypedArray、Map、Set、NodeList以及其他 DOM 集合),还包括 arguments 对象、由生成器函数生成的生成器,以及用户定义的可迭代对象
```js
const array1 = ["a", "b", "c"];
for (const element of array1) {
console.log(element);
}
```
* **支持所有可迭代对象**`for...of` 不仅可以遍历数组,还可以遍历其他可迭代对象,如 `Map``Set`、字符串等。
* **支持 `break` 和 `continue`**:在 `for...of` 循环中,你可以使用 `break` 来提前终止循环,或者使用 `continue` 跳过当前迭代。
* **支持异步操作**:在 `for...of` 循环中,你可以使用 `await` 来处理异步操作,而 `forEach` 不支持。
`for .. in` 是 es5 就有的, 专门用来遍历对象的. 也可间接通过 `Object.keys(obj)` 来遍历
```js
const object = { a: 1, b: 2, c: 3 };
for (const property in object) {
console.log(`${property}: ${object[property]}`);
}
```
## import export
```js
// import * as from 路径
import * as dayjs from 'dayjs'
// export * form 路径
export * from "./demandConfirmRecord"
```
## 模块化
`type="module"`
将 JavaScript 程序拆分为可按需导入的单独模块的机制
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="libs/user.js" type="module"/>
</head>
<body>
<h1>模块化测试</h1>
</body>
</html>
```
```js
const user = {
username: "张三",
age: 18
}
const isAdult = (age)=>{
if (age > 18){
console.log("成年人")
}else {
console.log("未成年")
}
}
export {user,isAdult}
// Java 怎么模块化;
// 1、druid.jar
// 2、import 导入类
// JS 模块化;
// 1、 xxx.js
// 2、 xxx.js 暴露功能;
// 3、import 导入 xxx.js 的功能
//xxx.js 暴露的功能,别人才能导入
```
## import() 动态导入
* 动态导入是 ES2020 引入的功能,允许在运行时动态加载模块。
* 使用 `import()` 函数,它返回一个 `Promise`,解析为模块的命名空间对象。
* 动态导入的路径可以是变量或表达式。
特点:
* **异步加载**`import()` 是异步的,不会阻塞主线程。
* **按需加载**:可以在需要时加载模块,减少初始加载时间。
* **条件加载**:可以根据条件动态加载不同的模块。
```js
import { foo } from './module.js';
```
```js
const modulePath = './module.js';
import(modulePath)
.then(module => {
module.foo();
})
.catch(err => {
console.error('Failed to load module', err);
});
```