ES6整理
每一次更新都是为了完善javaScript这门语言
1 let 和 const 关键字
1.1 let 关键字
let 关键字的作用: let 关键字同 var 关键字一样,都是用来声明变量。
let 关键字声明的变量与 var 关键字声明的变量有哪些区别:
1. let 关键字不能重复声明变量 (不论变量使用 var 声明的还是 let 声明的,let 都不能重复声明)
2. let 声明的全局变量不会作为window的属性
3. let 声明的变量不会提升
4. let 声明的变量除了有全局作用域、函数作用域。还有块级作用域
1.2 const 关键字
const 关键字的作用:
1. const 关键字可以用来声明常量。
2. 常量是一种特殊的变量,一旦赋值无法修改。
const 声明的变量与 let 声明的变量的区别:
1. const 声明的变量,值不能修改
2. let 声明变量可以先不赋值,自定得到 undefined; const 声明变量的同时必须赋值。
const 声明的变量也具备 let 声明的变量的 4 个特点:
1. const 关键字不能重复声明变量
2. const 声明的全局变量不会作为window的属性
3. const 声明的变量不会提升
4. const 声明的变量除了有全局作用域、函数作用域。还有块级作用域
2 解构赋值
1. 解构赋值 用于从数组、对象中提取数据,赋值给一个或多个变量
2. 解构赋值 可以用于给变量赋值或者函数传参
3. 等号的右边需要写数组或者对象(可以是任何形式,变量、直接量、表达式)
等号的左边要求将变量写在数组或者或者对象结构中,并不是真正的数组或对象。
2.1 数组的解构赋值
1. 数组的解构赋值根据索引进行匹配
2. 可以解构纯数组,也可以解构伪数组(字符串、arguments、nodeList...)
// 1. 同时给多个变量声明并赋值
const age = 1000;
let [v1, v2, v3] = ['hello,', ['a','b','c','d'], age];
// 2. 同时修改多个变量的值
/*
等号右边: 负责提供数据,形式可以是数组直接量,也可以是变量(变量的值是数组)
等号左边: 将变量放入数组结构
*/
const data = ['刘姥姥', '马姥姥', '司马姥姥', '欧阳姥姥'];
[v1, v2, v3] = data;
// 3. 使用解构赋值 交换两个变量的值
[v1, v2] = [v2, v1];
// 4. 解构赋值不但可以用于给变量赋值, 还可以用于函数传参(函数传参本质上就是实参赋值给形参)
function fn([age, name, address]) {
console.log(name, age, address);
}
fn([19, '高小乐', '上海']);
fn(data);
// 5. 两边的数组结构不完全一致
const [name1, name2, name3] = ['小乐', '大乐'];
console.log(name1);
console.log(name2);
console.log(name3); // 自动得到 undefined
// 6. 解构赋值 左侧的变量可以指定默认值
let [num1, num2, num3=250] = [10,20];
console.log(num1);
console.log(num2);
console.log(num3); // 使用默认值
// 7. 解构格式复杂的数组;
// 同一个数组可以进行多种形式的解构
var arr = [
100,
['高小乐', 199],
[
100,
[10, 20]
]
];
const [n1, [n2, n3], [n4, [n5, n6]]] = arr;
const [a1, a2, a3] = arr;
// 8. 不但可以解构纯数组,伪数组也可以被解构
// 伪数组: arguments、nodeList、HTMLCollection、String 等
const msg = 'Hello,高小乐';
const [s1, s2, s3, s4, s5, s6, s7] = msg;
var btnItems = document.querySelectorAll('.btns button');
const [e1, e2, e3] = btnItems;
2.2 对象的解构赋值
1. 对象的解构负值根据属性名进行匹配
2. 对象的结构赋值可以解构所有类型的数据,因为一切皆对象
// 1. 对象的解构负值 按照属性名进行匹配
const data = {name:'高小乐', age: 123, address:'上海', job:'法师'};
const {name: n1, age:n2, address: n3} = data;
// 2. 对象的结构赋值,可以简写 左边: 属性名与变量名一致
// const {name:usernamename, age:age,address:address,job:job} = data;
const {name:username, age, address, job} = data;
console.log(username, age, address, job);
// 3. 对象的解构赋值 用于函数传参
function fn({name, age, address}) {
console.log(name, age, address);
}
fn(data);
fn({
name: '安妮',
age: 17,
address: '上海'
});
// 4. 对象解构赋值,可以设置默认值 没有顺序要求
const {name:v1='曹操', age:v2, grade:v3='110', address:v4, job:v5} = data;
// 5. 对于复杂一些对象 进行解构 (按照属性名进行解构, 变量位于属性值的位置,属性名是判断条件)
const obj = {
email: 'xiaole@qq.com',
nums: [100, 200],
prop: {
content: 'Hello ES6'
}
};
const {email, nums:[num1, num2], prop: {content}} = obj;
const {email: e, nums:n, prop: p} = obj;
// 6.一切皆对象 对象的解构赋值可以解构一切数据
const {length:len} = 'hello world'; // 字符串中有属性 length
console.log(len);
const {length, push, pop, age: a10} = [100,200,300,400];
console.log(length, push, pop, a10); // a10 的值是undefined,数组中没有 age 属性
3 字符串新增特性
3.1 模板字符串
什么是模板字符串?
使用反引号定义的字符串,称为模板字符串。
相对于使用单引号或双引号定义的字符串,模板字符串有如下特点:
1. 模板字符串中可以直接写换行,适合定义内容比较多且带有换行的内容的字符串
2. 字符串中可以非常方便的嵌套变量或者表达式,将变量或表达式写在 ${} 中
3.2 字符串实例新增方法
ES3 方法:
indexOf()
lastIndexOf()
slice()
substring()
substr()
split()
toUpperCase()
toLowerCase()
replace()
charCodeAt()
search()
match()
ES5 方法:
trim()
ES6 + 方法:
repeat() 重复字符串,参数指定重复的次数
includes() 判断字符串中是否包含某个值,第一个参数是要查找的值,第二个参数是起始查找的位置,默认值是 0 返回布尔值
startsWidth() 判断字符串是否以某个值开头,第一个参数是要查找的值,第二个参数是起始查找的位置,默认值是 0
返回布尔值
endsWith() 判断字符串是否以某个值结尾,参数是要查找的值
padStart() 将字符串补全到指定的长度,填充的内容在字符串前面。 第一个参数是目标长度,第二个参数是填充的 内容,默认值是空格(ES2017)
padEnd() 将字符串补全到指定的长度,填充的内容在字符串后面。 第一个参数是目标长度,第二个参数是填充的 内容,默认值是空格(ES2017)
trimStart() 去除字符串前面的空格(ES2019)
trimEnd() 去除字符串后面的空格(ES2019)
replaceAll() 替换字符串中所有指定的值 (ES2021)
4 数值新增特性
4.1 新增的二进制和八进制表示方式
// ES6 八进制数字的表示方式
0o100;
// ES6 二进制数字的表示方式
0b101001010
4.2 Number 构造函数本身新增的方法和属性
ES3(旧):
Number.MAX_VALUE
Number.MIN_VALUE
ES6+ (新)
Number.MAX_SAFE_INTEGER 获取JS中能表示的最大的安全整数
Number.MIN_SAFE_INTEGER 获取JS中能表示的最小的安全整数
Number.EPSILION 获取JS中可以表示的最小精度
Number.isNaN() 同全局函数 isNaN()
Number.isFinite() 同全局函数 isFinite()
Number.parseInt() 同全局函数 parsetInt()
Number.parsetFloat() 同全局函数 parsetFloat()
Number.isInteger() 判断是否是整数
Number.isSafeInteger() 判断是否是安全整数
整数范围在 -2^53 ~ 2^53 之间的正数视为安全整数,不包括两个端点。超过安全数范围的整数无法保证其计算精度。
4.3 Math 新增方法
ES3(旧):
Math.PI
Math.sqrt()
Math.pow()
Math.abs()
Math.floor()
Math.ceil()
Math.round()
Math.max()
Math.min()
Math.random()
ES6+ (新):
Math.trunc() 截取出数字中的整数部分(小数直接截掉)
Mthn.sign() 判断一个参数是正数、负数还是0.分别返回 1、-1、0
Math.cbrt() 计算一个数字的立方根
Math.hypot() 计算所有参数平方和的平方根
4.4 指数运算符 ** (ES2016)
2 ** 10; // 2 的 10次方
5 ** 6; // 5 的 6次方
4.5 新增原始数据类型 bigint (ES2020)
bigint 数据类型:
ES6 中新增的一种原始类型数据,使用 typeof 进行判断可以得到 bigint
bigint 类型的数据的表示方式:
5325345n; // bigint 类型的数据只能是整数
bigint 类型的数据的特点:
1. bigint 不能与其他类型的数据进行数学运算。
2. bigint 可以与其他类型的数据比较大小,比较大小的时候会自动类型转换
bigint 类型的数据的作用:
number 类型表示的数字,如果超过安全整数的范围,无法保证计算精度,此时建议使用 bigint 来表示较大的整数。
4.6 数字间隔符(ES2021)
45_345_897;
45_0000;
5 函数新增特性
5.1 新增的函数参数默认值的设置方式
function fn(arg1, arg2=默认值) {
}
5.2 rest 参数
什么是 rest 参数:
1. rest 参数可以获取函数中多余的实参(没有形参与之对应的实参),得到一个数组
2. rest 参数需要写在普通形参的后面,rest参数的名字是自定义。
3. rest 参数可以用来代替 arguments
rest 参数与 arguments 的区别:
1. rest 参数得到的是纯数组,arguments 是一个伪数组。
2. rest 参数获取的是多余的实参,arguments 获取的是所有的实参。
3. rest 参数需要声明, arguments 无需声明自动创建。
5.3 箭头函数
① 箭头函数的语法
// 1. 定义一个箭头函数
const fn01 = () => {};
// 2. 定义箭头函数 有参数也有函数体
const fn02 = (name, age=10) => {
console.log(`我叫${name},今年${age}岁`);
return 100;
}
// 3. 箭头函数 如果只有一个形参可以省略小括号
const fn03 = name => {
console.log(`我叫${name},跟我混吧!`);
}
// 4. 箭头函数 如果函数体内只有一条语句,且该语句是return语句,可以省略大括号和return
const fn04 = (num1,num2) => num1 + num2;
// 5. 箭头函数 省略小括号 省略大括号
const fn05 = num => num + 60;
② 箭头函数的特点
1. 箭头函数中this指向: 箭头函数中没有自己的this,如果在箭头函数中使用this,沿着作用域链向上查找
2. 箭头函数中不能使用arguments,可以使用 rest 参数代替
3. 箭头函数只能调用,不能作为构造函数被实例化
4. 箭头函数不能作为生成器函数
5.4 函数参数尾逗号(ES2017)
// 最后一个形参的后面加个逗号也不会报错 函数参数尾逗号
function fn(num1,num2,num3,) {
}
6 数组新增特性
6.1 扩展运算符
① 把数组拆分为逗号隔开的参数序列
扩展运算符 ... 写在一个数组的前面,可以将数组拆分为用逗号隔开的参数序列
// 1. 将数组转为参数序列作为函数的实参
const data = [123, 23, 345, 199, 78, 56, 120, 290];
// 取出数组中最大的元素
console.log(Math.max(...data));
// 取出数组中最小的元素
console.log(Math.min(...data));
// 2. 实现复制数组 利用扩展运算符
const arr1 = [100,200,300,400];
// 复制 arr2是arr2 arr1是arr1 新创建了数组,数组中元素与arr1一样
const arr2 = [...arr1];
② 把多个值合并到一个数组中(把参数序列变为数组)
扩展运算符放在变量的前面,变量的值可以得到参数序列转为的数组。
// 1. rest 参数
function fn(arg1, arg2, ...args) {
console.log(args);
}
fn(10,20,30,40,'高小乐', 100,200,true);
// 2.在数组解构赋值中使用
const [n1,n2,n3,...n4] = [100,200,300,400,500,600,700,800];
console.log(n1);
console.log(n2);
console.log(n3);
console.log(n4);
2.2 Array 构造函数本身新增的方法
Array.of() 创建新数组,参数会作为数组中的元素而不再是长度,参数数量可以是任意个
Array.from() 将伪数组转为纯数组
2.3 Array 实例新增的方法
ES3(旧方法):
concat()
join()
slice()
push()
pop()
unshift()
shift()
splice()
reverse()
sort()
ES5(旧方法):
forEach()
filter()
map()
some()
every()
reduce()
reduceRight()
indexOf()
lastIndexOf()
ES6+(新方法):
find()👍👍 返回第一个满足条件的元素,参数是回调函数,如果没有满足条件的元素,返回undefined
findIndex() 返回第一个满足条件的元素的索引,参数是回调函数,如果没有满足条件的元素,返回-1
fill() 将数组中的每个元素都替换为指定的值
keys() 返回由数组中元素的索引组成的遍历器对象
values() 返回由数组中元素的值组成的遍历器对象
entries() 返回由数组中元素的索引和值组成的遍历器
flat() 将多维数组拉平,参数指定拉多少层,默认值是1,可以设置为 Infinity 表示任意层。 ES2019
includes() 判断是否是否包含某个元素,可以指定开始查找的位置,返回布尔值。 ES2021
7 对象新增特性
7.1 属性简写
使用 {} 定义对象的时候,用变量表示属性值,如果变量名与属性名一致,可以简写:
const name = '高小乐';
const age = 12;
const address = '上海';
function getInfo(){}
const obj02 = {
name,
age,
address,
getInfo,
job: '法师'
};
7.2 方法简写
使用 {} 定义对象的时候,里面的方法可以简写:
{
// 普通形式定义方法
getName: function() {
console.log('getName')
},
// 箭头函数定义方法
getAge: () => {
console.log('get Age')
},
// 简写形式的方法
getInfo() {
console.log('getInfo');
},
getAddress() {
console.log('getAddress');
}
}
7.3 声明对象时用表达式作为属性名
使用 {} 声明对象的时候,可以在里面使用表达式来表示属性名:
{
'home-address': '上海',
address: '北京',
[prop]: '高小乐',
[20*6]: 'hello world'
};
7.4 super 关键字
super 关键字可以在对象的方法中使用,是系统定义好的变量,与 this 类似:
1. this 指向调用该方法的对象; super 指向该方法所属对象的原型
2. this 只与谁调用有关,是动态的; super 与谁调用该方法无关,只与方法声明的位置有关(方法声明在了哪个对象 中),是静态的。
3. this 可以在任何形式创建的函数中使用; super 只能在简写的对象方法中使用。
7.5 对象的扩展运算符 (ES2018)
① 把对象拆分为逗号隔开的键值对序列
const users = {
username: '高小乐',
age: 12,
address: '上海',
say() {}
};
// 1. 使用扩展运算符复制对象
const obj1 = {...users};
// 2. 合并对象 users 和 {getInfo() {}, getName(){}, getAddress{}}
const obj2 = {...users, ...{getInfo(){},getName(){},getAddress(){}, username:'安妮'}};
② 把键值对序列合并到一个对象中
// 用于对象的结构赋值
const {username, ...data} = users;
7.6 Object 构造函数本身新增的方法
Object.is() 对两个数据进行判等,类似于===,与 === 有两点区别:
① NaN 和 NaN 返回true, 0 和 -0 返回 false
Object.assign() 用于合并对象
将第二个参数的属性以及后面所有的参数的属性都合并到第一个参数中,并将第一个参数作为返回值
Object.keys() 返回对象中的属性名组成的数组
Object.values() 返回对象中的属性值组成的数组
Object.entries()返回对象中的属性名和属性值组成的二维数组
Object.fromEntries() entries()的逆运算,将符合格式的二维数组转为对象
Object.getPrototypeOf() 用于获取某个对象的原型
Object.setPrototypeof() 用于修改某个对象的原型
7.7 Object 的实例新增的属性
__proto__ 获取对象的原型
7.8 可选链运算符(ES2020)
?. 可选链运算符可以用于调用对象中的属性;会自动判断属性是否存在,如果不存在就不向下调用
const obj = {};
obj.getInfo?.();
/*
相当于
if (obj.getInfo) {
obj.getInfo();
}
*/
obj.info?.name;
/*
相当于
if (obj.info) {
obj.info.name;
}
*/
8 Class 语法
8.1 使用 Class 定义类(定义构造函数)
语法:
class User {
// 定义属性 属性会添加到实例本身
username = '小智';
age = 23;
eat = ()=> {};//箭头函数和普通方法写法,都是添加到实例对象身上
// 使用简写方式定义方法, 方法会添加到实例的原型上
say() {
};
drink() {
};
}
特点:
1. 使用 class 关键字定义的类本质上仍然是构造函数,使用 typeof 判断返回 function
2. 使用 class 关键字定义的类(构造函数)不能被调用,只能被实例化
3. 在 class 里面使用简写方式为实例设置的方法,会添加到实例的原型的
4. 在 class 里面只能定义属性和方法,如果有其他代码可以在方法内部写
8.2 类中定义构造器
语法:
// 定义类(构造函数)
class User {
// 声明属性(写在构造器方法里后 这里可写可不写)
username;
age;
// 构造器方法
constructor(username, age) {
this.username = username;
this.age = age;
}
// 方法
say() {
console.log(`我叫${this.username},今年${this.age}岁`);
}
}
//java语言构造函数是包含在类里面的,不是等价的
//而js中类 和构造函数是等价的了,换成class语法后 也模仿了那一套
//之前定义类的时候 可以传行参不让属性的值可变,但是 class语法的类不能传参,是在构造器constructor里传参
特点:
1. #构造器方法在实例化的时候被自动调用,实例化一次就调用一次。
2. #构造器方法可以接收到实例化所传的实参,构造器方法主要用于给属性赋初始值。
8.3 类中定义静态方法
语法:
class Person {
// 定义普通属性
address = '上海';
// 定义静态属性 (还不是正式语法)
static username = 'hello';
// 普通方法
eat() {}
// 定义静态方法
static drink() {}
}
// 在类的外部定义静态方法
Person.drink = () => {}
// 在类的外部定义静态属性
Person.job = '值';
特点:
所谓静态方法和静态属性就是类(构造函数)本身的方法和属性!
8.4 继承
① extends 关键字实现继承
语法:
// 定义父类
class User {}
// 定义子类并继承父类
class VipUser extends User {}
特点:
1. 子类可以继承父类中定义的属性和方法,并在此基础上添加自己的属性和方法。
2. 一个父类可以被多个子类继承,一个子类只能继承一个父类
3. #extends 语法本质上仍然是按照原型链的规则实现继承: 子类的实例的原型是父类的一个实例
② 方法和属性的重写
语法:
// 定义父类
class User {
name;
age;
constructor(name, age) {
this.name = name;
this.age = age;
//还可以写其他
}
addShopcart(product) {
console.log(this.name + '将' + product + '加入购物车!');
}
}
// 定义子类
class VipUser extends User {
// 定义子类实例自己的属性
address;
// 重写构造器方法
constructor(name, age, address) {
// 必须将父类中的构造器方法再次调用一遍,先调用再干其他
//若直接this.name=name 这样重新覆盖的去重写 会报错
super(name, age);
this.address = address;
}
//重写方法 重写逻辑就好
buy() {
console.log(this.name + '购买了商品!');
}
addShopcart() {
console.log(this.name + '加入购物车!');
}
}
特点:
1. 子类中定义的属性和方法如果与父类中定义的属性或方法重名,#子类中会重写继承下来的属性和方法
2. 如果子类中重写构造器方法,子类中构造器方法中必须先通过 super 关键字来调用父类的构造器方法,再进行其他操作
##自己整理:
1. 不管子类是否显示定义constructor构造方法,都会默认加一个,在new实例的时候自动调用
2. 该方法一般返回的是实例对象的this
3. 子类重写构造器方法时,必须现在其内调用super,此时super相当于父类的构造器方法
4. 内部会做一些处理,相当于 父类.prototype.constructor.call(this, props),借助父类的构造函数实现this指向子类的实例
此时子类有了与父类相同的属性和方法,可以再加工了,
派生的构造函数就指望父类来帮他创建一个this对象
③ super 关键字
super 关键字可以作为对象使用,也可以作为函数使用。
super 关键字作为对象使用,具有如下特点:
1. super 关键字写在使用 {} 声明对象时,里面简写形式定义的方法中
2. super 表示方法所属的对象的原型
super 关键字作为函数使用,具有如下特点:
1. 在子类的构造器方法中使用 super, 只能在构造器方法中使用,其他方法中不能使用。
2. 此时 super 表示父类的构造器方法(继承了某个类 就要进行很多初始化操作,所以再调用super)
3. 在子类的构造器方法中,要求 super 必须写在最前面
④ 继承内置类(内置构造函数)
如 Array、Object、Function、Date 等都是内置类(内置构造函数),是系统定义好的类。
// 定义类 继承 Array
class MyArray extends Array {
// 重写父类的构造器方法
constructor(...args) {//形参rest参数,将传进来的参数序列 转变成数组 子类和父类的参数一起
super(...args);// 实参。将数组拆分为参数序列给super去传参调用,然后执行下面代码 按理说是父类原来的参数,但是多传也没事
this.max = Math.max(...args);//这里就是用的刚刚拆解的参数序列
this.min = Math.min(...args);
}
}
9 Symbol 类型
Symbol 是 ES6 中新增的一种原始类型的数据,具有如下特点:
1. 可以作为对象的属性名,对象的属性名可以是字符串的形式也可以是symbol的形式
2. 作为原始类型数据,使用 typeof 判断,返回 symbol
3. 通过调用函数 Symbol() 可以创建一个 symbol 类型的数据,每调用一次 Symbol() 函数都会创建一个独一无二的 symbol 类型的数据。
//可以给symbol()加参数作为标识以区分
//symbol().description 获得参数标识
10 Set 和 Map
10.1 Set
新增的数据类型,类似于数组,但是里面的成员的值不能重复,必须是唯一; 相比于数组没有索引结构。
① Set 构造函数
// 创建一个空的 Set
new Set();
const arr = ;
// 根据数组,创建Set
new Set([10, 20,30,40,40,50,60,40,20,10,50, '30']);
// 创建Set 根据字符串(或者可遍历对象)
new Set('Hello world');
#通过可遍历对象或者数组 创建set类型数据
Set 构造函数只能实例化不能调用!
② Set 的实例的属性方法
size 获取Set中成员的数量
add() 添加一个成员
delete() 删除一个成员
has() 判断是否存在某个成员
clear() 清空
keys()
values()
entries()
forEach() 遍历出Set中所有的成员
③ Set 的应用
1. 保存一些不允许重复的数据
2. 利用 Set 进行数组去重 [...new Set([1,2,3,4,4,4,4])]
10.2 WeakSet
WeakSet 与 Set 类似,也是由不重复的值组成的集合,与 Set 有两点区别:
① WeakSet 中的成员只能是对象类型的数据,不能是原始类型,Set 中的成员可以是任意类型;
② WeakSet 不可遍历
① WeakSet 构造函数
// 创建 WeakSet
const ws1 = new WeakSet();
// 创建 WeakSet, 以数组作为参数
const s = new Set([10,20,30,40])
const ws2 = new WeakSet([{name:'xiaole'}, function(){}, [10,20,30], s, new Date(), s]);
// 创建 WeakSet, 以遍历对象作为参数
const ws3 = new WeakSet(document.all);
WeakSet 构造函数只能实例化,不能调用!
② WeakSet 实例的方法
add() 添加一个成员
delete() 删除一个成员
has() 判断某个成员是否存在
10.3 Map
Map 是新增的数据结构,类似于 Object,是由键值对(key-value)组成的集合,与对象比有如下区别:
对象中的键(属性名)必须是字符串或者symbol类型,而Map中的键可以是任意类型数据。
① Map 构造函数
// 创建空的 Map
const m1 = new Map();
// 创建 Map,以二维数组作为参数
const arr = [
[100, '小乐'],
[[100,200,300], 2000],
[Array, 250],
[Object, 350],
[Set, 450],
[Function, 550],
[Array, 750],
[[100,200,300], 3000]
];
const m2 = new Map(arr);
// 创建 Map,以可遍历对象作为参数
const s = new Set([[100,10],[200,30],[400,40]]);
const m3 = new Map(s);
//重复后面覆盖前面的
Map 构造函数只能实例化,不能调用!
② Map 实例的属性方法
size 获取到成员的数量
get(key) 根据键获取值
set(key,value) 添加或者修改成员
delete(key) 删除成员
has(key) 判断是否存在某个成员
clear() 清空
keys()
values()
entries()
forEach() 遍历出Map中所有的成员
10.4 WeakMap
WeakMap 与 Map 类似,也是由键值对组成的集合,相对于 Map,有两点不同: ① WeakMap 的键只能是对象类型,不能是原始类型; ② WeakMap 不可遍历
① WeakMap 构造函数
// 创建空的 WeakMap
const wm1 = new WeakMap();
// 创建 Map,以二维数组作为参数
const arr = [
[[100,200,300], 2000],
[Array, 250],
[Object, 350],
[Set, 450],
[Function, 550],
[Array, 750],
[[100,200,300], 3000]
];
const wm2 = new WeakMap(arr);
// 创建 Map,以可遍历对象作为参数
const s = new Set([[{},10],[{},30],[{},40]]);
const wm3 = new WeakMap(s);
WeakMap 构造函数只能实例化,不能调用!
② WeakMap 实例的方法
get(key) 根据键获取值
set(key,value) 添加或者修改成员
delete(key) 删除成员
has(key) 判断是否存在某个成员
11 新增的运算符
1.1 指数运算符
**
2**3=8
11.2 可选链运算符
?.
obj?.name //若obj不存在 会返回undefined 不会报错
11.3 空值判断运算符
?? 空值判断运算符类似于逻辑或||, 区别在于空值判断运算符组成的表达式,只有左边的操作数是null或者undefined的时候,才会以右边的操作数作为表达式的值
false || 100; // 100
0 || 100; // 100
'' || 100; // 100
null || 100; // 100
undefined || 100; // 100
·······························
false ?? 100; // false
0 ?? 100; // 0
'' ?? 100; // ''
null ?? 100; // 100
undefined ?? 100; // 100
11.4 逻辑赋值运算符
&&=
||=
??=
n1 &&= 200; // n1 = n1 && 200
n1 ||= 300; // n1 = n1 || 300;
n1 ??= 400; // n1 = n1 ?? 400;
12 遍历器 iterator
12.1 iterator 遍历器对象
什么是遍历器对象?
iterator(遍历器对象)是一种接口,为各种不同的数据提供统一的访问机制,任何数据只要部署了 iterator 接口就可以进行遍历操作。
遍历器对象的特点?
1. 每个遍历器都有一个 next() 方法
2. 遍历器对象内部存在一个指针,初始指向遍历器对象中的第一个数据,调用 next() 会取出当前指针指向的数据,并且指针下移。
3. 每次调用 next() 方法,返回对象,对象中包含 value 属性 和 done 属性, value 属性就是当前指针指向的数据的值,done 属性是一个布尔值,表示是否结束遍历。
4. 遍历器对象(也叫iterator接口) 遍历结束后 指针已经不指向数据了,并且done也变成true了,不可再遍历了
//数组 和可遍历对象 是可以重复遍历的
得到遍历器对象的方法:
数组实例: keys() values() entries()
Set实例: keys() values() entries()
Map实例: keys() values() entries()
...
#实际应用是不需要将遍历器对象取出来使用的,旨在了解原理
12.2 iterable 可遍历对象
① 什么是可遍历对象
1. 把部署了 iterator 接口(遍历器接口)的数据结构称为 iterable(可遍历对象)
2. iterator 部署在了可遍历对象的 Symbol.iterator 属性上,#该属性是一个方法,调用这个方法返回一个遍历器对象(也叫iterator接口)
② 内置的可遍历对象
Array 的实例
Set 的实例
Map 的实例
字符串
arguments
NodeList
HTMLCollection
....
③ 哪些情况会调用可遍历对象的遍历器接口
1. 使用 for of 遍历可遍历对象
//自动调用可遍历对象的Symbol.iterator方法,返回一个遍历器对象,并通过for of循环调用next方法返回({value:值,done:布尔值}),并取出里面的值
//总结:如果我需要对可遍历对象进行遍历,那么就要调用其内部的遍历器接口(会内部处理)
2. 数组的解构赋值,所有可遍历对象都可以被解构
3. Array.from() 该方法可以把可遍历对象转为数组
4. 使用扩展运算符将可遍历对象分割为逗号隔开的参数序列
5. Set 构造函数的参数,要求是可遍历对象
6. WeakSet 构造函数的参数,要求是可遍历对象
7. Map 构造函数的参数,要求是可遍历对象
9. WeakMap 构造函数的参数,要求是可遍历对象
9. Promise.all() 的参数
10. Promise.race() 的参数
....
#{}声明的对象 内部没部署遍历器接口,所有他不是可遍历对象 是无序的
for in //不会调用遍历器接口, 数组和对象都可以用,拿到的是索引和属性名
④ 可遍历对象(iterable)和遍历器对象(iterator)的关系
1. 所有的遍历器对象都是可遍历对象,可遍历对象不一定是遍历器对象。//遍历器本身也要遍历器接口,但是得到的就是他自己
2. 所有的可遍历对象都可以通过遍历器接口获取到与之对应的遍历器。
//arr[Symbol.iterator]() 或者const iter = arr.values(); 就得到一个遍历器接口
⑤ 可遍历对象(iterable)和伪数组的关系
1. 伪数组指的是像数组一样具有 索引(对应的下标)结构,由多个数据组成的#不是数组的数据类型
2. 可遍历对象指的是部署了遍历器接口的对象
3. 二者是完全不同的两个概念, String、Arguments、NodeList、HTMLCollection 既是伪数组又是可遍历对象; Set、Map 不是伪数组是可遍历对象。
12.3 for ... of
所有的可遍历对象(包括遍历器对象)都可以使用 for of 进行遍历。
13 生成器 generator
13.1 什么是生成器
1. 能够创建遍历器的函数称为生成器函数(generator)
2. 可遍历对象的遍历器接口(Symbol.iterator 属性的值)就是一个生成器函数 。
(自己补充)调用后返回一个遍历器对象,使用时,内部会再调用next方法,返回对应值
13.2 如何自定义生成器
function* 生成器名字() {
}
13.3 yield 关键字
function* 生成器名字() {
yield 值;
yield 值;
yield 值;
yield 值;
yield 值;
}
1. yield 关键字只能在生成器函数中使用
2. 调用生成器函数得到遍历器对象之后,调用遍历器对象的next()方法,得到yield 后面的数据,作为next()返回对象的value属性的值
3. 调用生成器函数的时候,只会得到一个遍历器对象,不会执行生成器中的语句; 只有调用遍历器对象的 next()方法的时候,才会执行生成器中的语句,执行到 yield(yield之间的会执行) 会停下来,再调用 next() ,再执行到下一次 yield,
4. 生成器中的 return,可以结束遍历器的遍历
13.4 利用生成器给对象部署 iterator 接口(自定义可遍历对象)
const obj = {
name: '高小乐',
age: 18,
address: '上海',
users: ['刘姥姥', '马姥姥', '欧阳姥姥', '司马姥姥'],
say: ()=>{}
};
// 给 obj 部署一个遍历器接口
obj[Symbol.iterator] = function*(){//相当于在obj身上加了一个属性 这个属性是一个方法,调用该方法只会返回遍历器对象不会执行里面代码
for (let i in obj) {//对象可以用for in 循环 遍历 ,数组也可以,不过返回的是下标
yield [i, this[i]]; //因为是obj的方法,obj调用,this就是obj
}
};
14 模块
14.1 定义模块(模块中导出数据)
第一种导出数据的方式:
// module1.js 模块一
export var firstName = 'Micheal';
export var lastName = 'JackSon';
export function play() {}
第二种导出数据的方式:
// module2.js 模块二
function say() {}
function eat() {}
let name='zs'
export default {
say,
eat,
name
};
//常用
14.2 导入模块(使用模块)
// 导入模块一 该模块中采用的是第一种导出数据的方式
import {firstName, lastName, play} from './module.js';
// 导入模块二 该模块中采用的是第二种导出数据的方式
import myModel from './module2.js'
1. 第一种导入方式,import 后面的变量名必须与模块中导出的数据保持一致
2. 第二种导入方式,import 后面的变量名是可以自定义的 //常用
4 总结
4.1 ECMAScript 中的数据类型
原始类型(值类型): number、string、boolean、null、undefined、bigint、symbol (7种)
对象类型(引用类型):Array、Object、Function、Date、Set、WeakSet、Map、WeakMap ...
//自定义一个构造函数 就等于新定义了一个数据类型 所以以上针对js内置的对象类型
4.2 ECMAScript 中定义变量的方式
一共 6 种:
1. var
2. function
3. let
4. const
5. class
6. import
let、const、class、import 是ES6新增的,新增的这4种关键字定义的变量,具有如下特点:
① 不能重复声明
② 不会成为window的属性
③ 不会提升
④ 具有块级作用域
4.3 数组偏平化(拉平)
// 第一种方式 ES6新增的数组实例的方法 flat
arr.flat(Infinity));
// 第二种方式 使用join将数组合并为字符串,再使用split将字符串分割为数组
// 缺点: 处理完成后,数组中的元素都会变为字符串
arr.join().split(',');
// 第三种方式 通过递归函数
function arrayFlat(data) {
// 创建一个新的空数组
let res = [];
// 遍历原数组
for (let i = 0; i < data.length; i ++) {
// 判断原数组中的元素是否还是数组
if (data[i] instanceof Array) {
// data[i] 还是数组,递归调用,递归调用的结果连接到新数组
res = res.concat(arrayFlat(data[i]))
} else {
// data[i] 不是数组,直接添加到新数组中
res.push(data[i]);
}
}
// 返回新数组返回
return res;
}
//解决问题的时候可以先从简单情况找出规律
4.4 对象的浅拷贝
浅拷贝:
将一个对象拷贝到一个新的对象中,但是新的对象的属性a是一个引用类型,那么修改原来的a属性的值,也会影响新的拷贝的对象
数组的浅拷贝:
1. [...arr] 扩展运算符
2. arr.concat() 返回新的数组
3. arr.slice() 返回新的数组,从头截取到尾
4. Array.from(arr) 返回新的数组
对象的浅拷贝:
1. {...obj} 扩展运算符
2. Object.assign({}, obj) 返回新对象,利用对象合并实现对象浅拷贝
4.5 对象的深拷贝
// 专门判断对象的类型
function getObjectType(data) {
return Object.prototype.toString.call(data).slice(8, -1);
}
// 引用类型 instanceof Object的判断 都是true 所以 没法判断到底是不是狭义上的object
//像 数字 和字符串 对象等的toString方法 都是转换成对应的字符串 ,只要Object 原型对象上的可以完美判断
// 实现对象的深拷贝
function deepClone(data) {
// 判断data是Object是数组还是其他
if (getObjectType(data) === 'Object') {
var res = {}; // 创建新的空对象
} else if (getObjectType(data) === 'Array') {
var res = []; // 创建新的空数组
} else {
return data;
}
// 对传入的data进行遍历
for (let i in data) {//对象和数组都可以用这个方法 但是和遍历器无关
res[i] = deepClone(data[i]);
// 前面的都是普通属性值 调用的时候 直接走到else里 返回deepClone(data[i]) 并赋值给res,
// 当循环到child属性的时候 是一个对象,又会创建一个新的对象,并且执行循环 并赋值
}
// 返回结果
return res;
}
\