14 KiB
ES6
参考: 阮一峰es6 https://wangdoc.com/es6/
let const
推荐使用let const 关键字替代 var关键字声明变量,因为 var存在诸多问题
- 越域
{
var a = 1;
let b = 2;
}
console.log(a); // 1
console.log(b); // ReferenceError: b is not defined
- var 可以声明多次
- var 会变量提升
解构
把复合数据类型(对象居多, 数组较少)快速解析成单个变量,而无需使用arr[x]或者obj[key]等传统方式进行赋值
数组解构赋值
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
// 如果想跳过某个元素, 只需多写一个逗号
嵌套数组解构
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
函数传参解构
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循环解构
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
对象赋值解构
对象的解构必须要用属性名, 不是根据顺序, 而是属性名对应
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 }
对象传参解构
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
解构重命名变量
var obj = {
name: 'chris',
sex: 'male',
age: 26
};
var {name: nickname, age: howold} = obj;
console.log(nickname + ' ' + howold); //chris 26
对象的解构赋值是下面形式的简写
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' }
对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' }
baz // "aaa"
foo // error: foo is not defined
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 不是变量。
嵌套对象解构
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);
对象属性重名
需要更改变量名:
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 大熊
循环解构对象
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
定义默认值
在解构过程中,如果某个路径上的属性不存在,可以使用默认值。
const user = {
id: 123,
name: 'hehe'
};
const {
education: {
school: {
name
}
} = {
school: {
name: 'NB'
}
}
} = user;
console.log(name); //prints: NB
解构的特殊应用场景
//变量互换
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
展开运算符
展开运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值
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
特殊应用场景:
//数组深拷贝
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' ]
剩余运算符
剩余运算符也是三个点号,不过其功能与展开运算符恰好相反,把逗号隔开的值序列组合成一个数组
//主要用于不定参数,所以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 ]
剩余运算符配合解构使用:
var [a, ...rest] = [1, 2, 3, 4];
console.log(a);//1
console.log(rest);//[2, 3, 4]
结构展开运算符小结
等号表达式是典型的赋值形式,函数传参和for循环的变量都是特殊形式的赋值。解构的原理是赋值的两边具有相同的解构,就可以正确取出数组或对象里面的元素或属性值,省略了使用下标逐个赋值的麻烦。
==对于三个点号,三点放在形参或者等号左边为剩余运算符; 放在实参或者等号右边为展开运算符。==
结构展开运算符经验
- 在等号赋值或for循环中,如果需要从数组或对象中取值,尽量使用解构。
- 在自己定义函数的时候,如果调用者传来的是数组或对象,形参尽量使用解构方式,优先使用对象解构,其次是数组解构。代码可读性会很好。
- 在调用第三方函数的时候,如果该函数接受多个参数,并且你要传入的实参为数组,则使用展开运算符。可以避免使用下标形式传入参数。也可以避免很多人习惯的使用apply方法传入数组。
- 剩余运算符使用场景应该稍少一些,主要是处理不定数量参数,可以避免arguments对象的使用。
可选链操作符
读取对象内部的某个属性,往往需要判断一下,属性的上层对象是否存在,当其中一链为null或者undefined时就返回undefined,这样即使中间缺少一个属性也不会报错
const firstName = message?.body?.user?.firstName || 'default';
空值合并操作符
空值合并操作符(
??
)是一个逻辑操作符,当左侧的操作数为null或者undefined时,返回其右侧操作数,否则返回左侧操作数。
如果使用 ||
来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,''
或 0
)时。
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 是空值)
函数的默认值
function show(msg = "...") {
console.log(msg)
}
show()
show('test')
- 如果一个函数有多个形参也有默认值: 默认值一定要在后面
function test(a, b, c =1){}
// 不能是 function test(a=1, b, c){}
箭头函数/lamda表示式
// 两个参数的情况:
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
模板字符串
`${变量}`
let info = "你好,我的名字是:【"+name+"】,年龄是:【"+age+"】,邮箱是:【】"
console.log(info);
# 模板字符串的写法
let info = `你好,我的名字是:${name},年龄是:${person.age},邮箱是:${person.email}`
console.log(info);
fetch api
另建笔记
Promise
另建笔记
async
另建笔记
Set
可以理解为不重复的数组
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 没有元素的序列化和解析的支持
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
的值都是唯一的, 即使他们的描述相同 - 可以用于对象的键名, 避免冲突
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
混合运算
const big = 9007199254740992n
console.log(big)
console.log(big + 1n)
Class
另建笔记
Proxy
创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
const p = new Proxy(target, handler)
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 对象、由生成器函数生成的生成器,以及用户定义的可迭代对象
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)
来遍历
const object = { a: 1, b: 2, c: 3 };
for (const property in object) {
console.log(`${property}: ${object[property]}`);
}
import export
// import * as from 路径
import * as dayjs from 'dayjs'
// export * form 路径
export * from "./demandConfirmRecord"
模块化
type="module"
将 JavaScript 程序拆分为可按需导入的单独模块的机制
<!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>
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()
是异步的,不会阻塞主线程。 - 按需加载:可以在需要时加载模块,减少初始加载时间。
- 条件加载:可以根据条件动态加载不同的模块。
import { foo } from './module.js';
const modulePath = './module.js';
import(modulePath)
.then(module => {
module.foo();
})
.catch(err => {
console.error('Failed to load module', err);
});