ES6语法入门

文章摘选自https://blog.csdn.net/weixin_38800668/article/details/109365551

什么是ES6?

ES 的全称是 ECMAScript , 它是由 ECMA 国际标准化组织,制定的一项脚本语言的标准化规范。ES6 实际上是一个泛指,泛指 ES2015 及后续的版本。

ES6 的新增语法

let 命令 用于声明局部变量

{
let a = 1;
var b =2;}
}
a //调用失败 let 声明的变量的为局部变量,只在{....}的块作用域生效。
b //2 var 声明的变量为全局变量,在全局作用域生效。

const 命令

const 命令 用于声明一个只读常量,一旦声明了就不能改变。const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

const a = 10;
a = 20; //再次赋值失败 const创建的变量为常量,一旦声明了就不能改变

变量提升与非变量提升

变量提升指的是变量的作用域提升。常表现为:
var 声明变量可以先使用后声明,值为undefined。

而非变量提升指的是变量需要 先声明后使用,区别于var 声明的变量。常变现为:let声明的变量。

let、const、var 的区别

  • 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象。
  • 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升。
  • 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升。

解构赋值

ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构。

数组解构

 let [a, b, c] = [1, 2, 3];
 console.log(a) //1
 console.log(b) //2
 console.log(c) //3

如果解构不成功,变量的值为undefined

对象解构

let person = { name: 'zhangsan', age: 20 };
let { name, age } = person;
console.log(name); // 'zhangsan'
console.log(age); // 20

let {name: myName, age: myAge} = person; // myName myAge 属于别名
console.log(myName); // 'zhangsan'
console.log(myAge); // 20

解构赋值能指定默认值?

解构赋值可以指定默认值,使用 = 运算符

let [a,b=3] = [1]
a //1
b //3

let [a,b=3] = [1,2]
a //1
b //3

解构赋值能起别名?

解构赋值可以起别名,使用 :运算符

let [a : one,b = 2] = [1]
one //1
b //2

新增字符串的方法

对象方法:String.fromCodePoint( )

ES5 提供String.fromCharCode()方法,用于从 Unicode 码点返回对应字符,但是这个方法不能识别码点大于0xFFFF的字符。

ES6 提供了String.fromCodePoint()方法,可以识别大于0xFFFF的字符。语法如下:

String.fromCodePonit('\u{D834}'); // "ஷ"

对象方法:字符串转义String.raw( )

ES6提供一个为字符串转义的方法。该方法返回一个斜杠前面再加一个斜杠的字符串。但实际显示的是被转义后的字符串。

常用于模板字符串,语法如下:

String.raw`Hello \n world`
//返回结果为 Hello \\n world
//实际返回的结果为 Helo \n world

 实例方法:includes(), startsWith(), endsWith()

传统上,JavaScript 只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let text = 'Hello ! ES 6';
text.includes('Hello'); //true
text.startsWith('H'); //true
text.endsWith('6'); //true

实例方法:repeat( )

repeat( args)方法表示,将一个字符串重复几次,并返回该新字符串。 args代表重复的次数,如果是小数则会向上取整。

‘q’.repate(3);  //qqq
'mm'.repeat(3); //mmmmmm

实例方法:padStart(),padEnd()

ES6 2017版本,提供了一个字符串两个补全方法。

padStart( num,parm) 方法:基于首位补全字符,num为补全后的字符串长度,parm为补全的字符。

'c'.padStart(3,'ab'); //abc

padEnd(num,parm)方法:基于末尾补全字符,num为补全后的字符串长度,parm为补全的字符。

'c'.padEnd(3,'ab'); //cab

实例方法:trimStart( )、trimEnd( )

ES6 2019版本提供了两个去除空格的方法,trimStart( )、trimEnd( ),其行为是trim( )是一致的。

const text = ' abc ';
a.trim(); //'abc'
a.trimStart(); //'abc '
a.trimEnd(); //' abc'

实例方法:matchAll( )

matchAll( ) 一个字符串中正则匹配的所有字符子串。

实例方法:replaceAll( )

replaceAll (‘被替换的字符’,‘要替换的字符’ ) 是ES 6新推出的实例方法,用于替换所有的指定字符。

对比以往的replace( )方法,以往的方法只能替换第一个的匹配的结果。而replaceAlll( )则替换所有。

const text = 'abca';
text.replace('a','1'); //1bca
text.replaceAll('a','1'); //1bc1

正则的拓展

RegExp 构造函数

ES 5标准下:

const regex = new RegExp(/Abc/i)
regex //Abc  其中i为修饰符,代表着不区分大小写进行匹配。

ES 6标准下:

# 在ES 6的标准下,RegExp构造函数被允许写入两个参数。

第一个参数为被匹配串,第二参数为修饰符

const regex = new RegExp(/Abc/,'i')  //在ES 5中会出错
regex //Abc
const regex = new RegExp('Abc','i');
regex //Abc

RegExp新增属性

RegExp.prototype.flags

返回该正则的修饰符。

let text = new RegExp(/Abc/,'i').flags;
text // i

RegExp.prototype.Unicode

返回是否设置了u修饰符的布尔值。

let boolean = new RegExp(/abc/,'u');
boolean // true
let boolean2 = new RegExp(/abc/)
boolean //false

RegExp.prototype.sticky

返回是否设置了y修饰符的布尔值。

let boolean = new RegExp(/abc/,'y');
boolean // true
let boolean2 = new RegExp(/abc/)
boolean //false

数值的拓展

Number 对象的拓展

Number.isFinite( )

该方法用于检查一个数是否有限,返回true或false。

Number.isFinite(100) //true
Number.isFinite(NaN) //false

Number.isNaN( )

该方法用于检查一个数是否为NaN,返回true或false。

Number.isNaN(100) //false
Number.isNaN(NaN) //true

Number.parseInt( )、Number.parseFloat( )

ES6上的Number.parseInt( )、Number.parseFloat( ) 行为与ES5 保持一致,都是分别将数转为整型和转为浮点型。

只不过在ES6后,将该方法移植到ES6上。

Number.parseInt(3.4) //3
Number.parseFloat(3) //3.0

Number.isInteger( )

该方法用来判断一个数是否为整数。

如果对数据精度的要求较高,不建议使用,如果一个数低于javaScript认知,则会默认强制转换为0。

Number.isInteger(3.4) //false
Number.isInteger(3) //true
Number.isInteger(3.0) //true

Number.isSafeInteger( )

这个方法用于检验某个数是否为安全整型,返回true和false。

JavaScript 能够准确表示的整数范围在-2^53 到 2^53 之间(不含两个端点),超过这个范围,无法精确表示这个值。

Number.isSafeInteger(13) //true
Number.isSafeInter(NaN) //false

BigInt 数据类型

JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制:

一:是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。

二:是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity。

为了解决上述的问题,ES6 2020版本推出了BigInt数据类型。用于表示2的1024次方或较大的数据。

语法如下:

# 与普通Int的区别:
1、BigInt需添加后缀n
const num1 = 15346349309;
const num2 =  15346349309n;
num1 //Infinity
num2 //15346349309
#与普通Int的区别:
2、Int类型和BigInt类型数值并不相等
123 === 123n //false
typeof 123n //'BigInt'
#BigInt不能使用 + 号来表示整数

函数的拓展

在ES5之前,并不能为函数直接指定默认参数。只能通过变通传入参数,如果传入的参数为空,则处理成默认值。这样方式去解决。

在ES6后,就可以指定参数的默认值了。语法如下:

function sayHi(val='jack'){
console.log('Hello'+val);
}
sayHi(); //hello jack
sayHi(Bob); //hello Bob
# 函数的参数还可以和解构赋值一起使用
function add(x,y,z=3){
return x + y + z;
}
add([1,2]); //6
add({x:1,y:2}); //6
# 在指定了参数默认值函数上length属性
# 此时length属性将返回没有指定 默认值的参数个数
const num = function(a){}.length; //num = 1
const num = function(a=1){}.length; //num = 0
const num = function(a=1,b,c){}.length; //num = 2
# 指定参数的默认值为 抛出不能省略该参数错误 的函数
function parError(){
return new Error('该参数不能省略')
}
function add(val=parError()){
return 'Hello ' + val;
}
add(); // 该参数不能省略
# 指定参数的默认值为 Undefined 表明该参数可以省略

箭头函数

在ES6中 允许使用 => 定义函数,甚至省略function 、return等关键字。便于简化函数。

基本语法:

# 无参数箭头函数
var x = ()=> 1;
var y = function(){
return 1;
}
//x 与 y等同
# 一个参数的箭头函数
var x = x=>x;
var y  = function (x){
return x;
}
x(1)
y(1);
//x 与 y等同
# 多个参数的箭头函数
var x = (a,b)=>a+b;
var y = function (a,b){
return a+b;
};
x(1,2); //3
y(1,2); //3
# 只有一条语句且没有返回值的箭头函数
let fn = () => void doesNotReturn();
# 箭头函数与解构函数结合使用
let fn1 = ({age,sex}) = > console.log(age,sex);
fn1(person);
let fn2 = function(person){
console.log(person.age,person.sex);
}
fn2(person);

使用注意点

箭头函数有几个使用注意点。

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

数组的拓展

拓展运算符

基本介绍与用法

拓展运算符是三个点 (…),它好比Rest参数的逆运算,将数组转换为以逗号分隔的参数序列。

# 一般性写法
console.log(...[1,2,3]) //1,2,3
# 分隔数组为以逗号分隔的参数序列
function add(x){
return x + y;
}
let value  = add(...[3,7]);
value; //3+7 = 10

 应用:数组复制

在以往,一个数组的复制,如果采用直接的复制。会得到指向同一份数据的另一个指针。如数组B直接复制数组A,数组B保存的数据并不是数组A的源数据,而是指向和数组A同一份数据的另一个指针。

弊端:根据上面,数组B和数组A同时指向同一份数据,如果数组B改变该数据时,数组A也会跟着改变。因为同时指向同一份数据。

后来为了ES5为了解决上述问题,便可以通过concat( )的方法对两个数组进行拼接,如数组A和数组B进行拼接,数组A则会以其本身数据的 “一份拷贝” 与B进行拼接。拼接成功后,无论B怎么修改,也不会影响数组A。但ES6使用拓展运算符,将更加简洁。

语法如下:

# ES5写法
var A = [1,2,3];
var B = A.concat(); //以A的一份数据拷贝进行拼接
B.push(4);
B;//[1,2,3,4]
A;//[1,2,3]
# ES6写法
var A = [1,2,3];
var B = [...A];

应用:数组合并

可以使用拓展运算符 (…),对两个数组进行合并。但需要注意的是,该合并都是基于浅拷贝。所谓的 浅拷贝 指的是浅复制是指当对象的字段值被复制时,字段引用的对象不会被复制。即复制前后,都是引用同一个对象。如果原数组引用发生改变时,会直接反映新数组上。

var A = [1,2,3];
var B = [4,5,6];
var C = [...A,...B];
C; //[1,2,3,4,5,6];
A.push(0);
# 当A发生改变后
# 因为数组C的部分数据字段引用对象是原数组A,
# 所以原数组A发生改变时,会直接反映到C数组上。
C; //[1,2,3,0,4,5,6];



数组对象多个方法的拓展

Array.form( )

该方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

var point = {
'0':'x',
'1':'y'
}
# ES5的写法
var arr = [].slice.call(point);
arr;//['x','y']
# ES6的写法
var arr = Array.form(ponit);
arr;//['x','y']

Array.of( )

该方法用于将一组值转换为数组。

# ES5的写法
var A = 12345;
var arr = A.split(',');
arr;//[1,2,3,4,5]
# ES6的写法
var A = 12345;
var arr = Array.of(A);
or 
var [..arr] = A;
arr;//[1,2,3,4,5]

数组实例多个方法的拓展

copyWithin( )

使用该方法,从数组内部指定位置复制一个数到另一个位置上,会覆盖原数据,并返回新的数组。

语法如下:

# Array.prototype.copyWithin(target, start = 0, end = this.length)
# 参数说明:必需参数:target为从该位置开始替换数据。
       可选参数:start为开始数据拷贝位置,默认为0,end为数据停止拷贝位置,默认为长度。
var arr = [1,2,3,4,5,6];
var newArr = arr.copyWithin(0,2,3); //意为拷贝2-3索引位置上的数据,并从索引位置0开始替换
newArr; //[3,4,3,4,5,6] 覆盖了1和2   

find( ) 和 findIndex( )

数组实例方法 find( )方法用于找出第一个符合条件的数组成员。

语法如下:

# 参数说明
# find( )方法参数为一个回调函数。每个数组成员依次执行该回调函数
# 返回值说明
# 如果找出符合条件的第一个数组成员,则返回该成员。
# 否则返回 undefind
var arr = [1,2,3,4,5];
var value = arr.find((val)=>val==3);
value;//3
var value2 = arr.find((val)=>val<0);
value2;// undefind

数组实例方法 findIndex( )

用于找出一个符合条件的数组成员的 位置。

语法如下:

# 与 find( )方法的区别
# find( ) 找到返回成员,找不到返回Undefind。
# findIndex( ) 找到返回成员的索引位置,找不到返回-1
var arr = [1,2,3,4,5];
var value = arr.findIndex((val)=>val==3);
value;//2
var value2 = arr.findIndex((val)=>val<0);
value2;// -1

fill( )

数组实例 fill( )方法用于以指定值填充数组。

语法如下:
# 参数说明
# fill( ) 方法第一个参数为填充值。(必需)
# fill( ) 方法还可以指定另外两个参数,用于指定填充起始和结束位置。(可选),默认为全部填充
# 指定一个参数时,默认为全填充
var arr = [1,2,3].fill('A');
arr;//['A','A','A']
# 指定多个参数
var arr = [1,2,3].fill(7,1,2);
arr; //[1,7,3];

entries( ) 和 key( )、values( )

ES6 提供三个新的方法entries(),keys()和values()用于遍历数组。

语法如下:
# 参数说明
# 三个方法均返回Itertor对象,配合for of循环对数组元素进行遍历
# entries( )对键与值进行遍历
# keys( )对键进行遍历 values( )对值进行遍历
let arr = ['a','b','c'];
for(let [index,value] of arr.entries( )){
console.log(index,value); //0:a,1:b,2:c
}
for(let key of arr.keys()){
console.log(key); //0,1,2
}
for(let value of arr.values()){
console.log(value); //'a','b','c'
}

 includes( )

该方法用于查找数组中是否存在某个值。

在ES5的版本用include( ) 去寻找某个值是否存在数组中,但由于include( )的比较为 ===,可能会对NaN进行误判。由此ES6新提出了Includes( ) 用来查找数组中是否存在某个值。

语法如下:

# 参数说明
# inclues( )方法第一个参数为搜索值。(必需)
# 第二个参数和第三个参数分别为开始搜索位置和结束搜索位置。(可选)
# 返回值说明
# 找到返回true 反之返回 false
[1,2,3].includes(3);//true
[1,2,3].includes(4);//false

数组扁平化处理 flat( ) 和 flatMap( )

数组有时候成员还是数组,为了解决嵌套的问题,ES6提出了flat( )方法,数组的实例方法 flat( ) 用于将 数组中的数组成员进行扁平化处理,将该成员取出并加到原有的位置上。

flat( ) 方法语法如下:

# 参数说明  如果原数组有空位,flat()方法会跳过空位。
# flat(args) 参数用于指定 扁平化层数。默认为1
# 如果扁平化多层,都要转成一维数组。可以用Infinity关键字作为参数
# 返回值说明
# 该方法返回一个新数组,对原数组无影响.
# 扁平化一层
var arr1 = [1,[2,3],4].flat();
arr1;//[1,2,3,4]
var arr2 = [1,[2,3],[4]].flat();
arr2; //[1,2,3,4]
# 扁平化两层
var arr2 = [1,[2,3],[4,[5,6]]].flat(2);
arr2; //[1,2,3,4,5,6]
# 扁平化三层
var arr3 = [1,[2,3],[4,[[5],6]]].flat(3);
arr3; //[1,2,3,4,5,6]
# 扁平化多层
var arr4 = [1,[2,3],[4,[5,6,[7,[8,9]]]]].flat(Infinity);
arr4; //[1,2,3,4,5,6,7,8,9]
# 如果原数组有空位 flat()方法会跳过空位。
var arr5 = [1, ,3,4].flat();
arr5; //[1,3,4]

flatMap( )与flat( )功能一致。

flatMap( )

用于将每个数组成员执行一个函数,并对该函数的返回值执行一次flat( )方法。

flatMap( ) 语法如下:

# 参数说明
# flatMap(function(){.....}) 参数为一个历遍函数,每个数组成员执行一次。
# 然后对该函数的返回值执行一次 flat()函数。
# 返回值说明
# 返回一个新扁平化数组,不影响原数组。
# 注意
# flatMap()只能展开一层数组。
//该方法相当于 [[1,2],[2,3],[3,4]].flat();
var arr  = [1,2,3].flatMap((x)=>[x,x+1]);
arr;//[1,2,2,3,3,4]
#flatMap()方法的参数是一个遍历函数,该函数可以接受三个参数,分别是当前# 数组成员、当前数组成员的位置(从零开始)、原数组。
//注意这里展示的语法 中括号[] 代表该参数为可选参数,如[,thisArg] 即为可省参数
arr.flatMap(function callback(currentValue[, index[, array]]) {
  // ...
}[, thisArg])
# flatMap()方法还可以有第二个参数,用来绑定遍历函数里面的this

对象的拓展

对象的拓展运算符

拓展运算符(…)同样能在对象中作用,对对象进行复制和合并等操作。

语法如下:

let age = 18,sex = 'fmale';
let el = {age,sex};
# 复制对象
let e2 = {...el};
# 合并对象
let age2 = 19,sex2 = 'male';
let e3 = {age2,sex2};
let e4 = {...e1,...e3};
e4;//Object {age:18,age2:19,sex:'fmale',sex2:'male'};
# 注意
# 当原对象的自定义属性和合并对象的属性名重名时
# 原对象在前,合并对象在后。合并对象对应原对象的同名属性会覆盖。反之。
let age = 18,sex = 'fmale';
let el = {age,sex};
let e2 = {
age:19,
sex:'male'
}
let e3 = {...e1,...e2};
e3;//Object {age:19,sex:male} 前者覆盖后者

对象的新方法

Object.assign( )

用于将源对象的 ”可枚举对象“ 复制到目标对象中。

语法如下:

# 参数说明
# Object.assign(target,Object1,Object2....)
# 第一个参数为目标对象,其余参数为源对象
let target = {a:1,b:2};
let Object = {c:3,d:4}
let newObject = Object.assign(target,Object);
newObject; //{a:1,b:2,c:3,d:4}
# 注意:该拷贝 (复制)为浅拷贝。

Object.is( )

用于判断两个对象是否 “严格” 相等。与===类似

let x = 1;
Object.is(x,x); // true
Object.is({x},{x}); // false

新的数据结构 Set

ES6提供新的数据结构Set,Set结构类似如数组。但成员都是唯一的。即无重复值。

这里需要注意,Set 结构的键名就是键值(两者是同一个值),因此第一个参数与第二个参数的值永远都是一样的

基本用法

# 生成:使用 Set( )构造函数来生成Set数据结构。
let set = new Set( );
# 添加数据:使用 Set( ).add( )方法向Set数据结构添加数据
[1,1,2,3,4,5].forEach(v=>set.add(v);
# 遍历:因为Set继承了Iterable接口,所以可以使用for of循环进行遍历
for(value of set){
consolo.log(value);//1,2,3,4,5 无重复数
}
# 参数说明
# Set构造函数接受数值或数组或其他具有iterable接口的数据结构都可以
let set = new Set([1,2,3,4,5]);
# 应用场景
# 常用于将一个数组转换为无重复值的数据
let set = new Set([1,1,2,3,4,5]);
let arr = Array.form(set);
arr;//[1,2,3,4,5]
or
let arr = [..set];
arr;//[1,2,3,4,5];

Set的实例属性和方法

语法如下:

# Set结构的实例属性
# Set.prototype.constructor 指向构造函数
new Set() === Set.protoype.constructor
# Set.prototype.size() 读出Set结构的实例个数
new Set([1,2,3,4]).size(); //4
# Set结构的实例方法
# 常用的四个操作方法
# Set.prototype.add(value) 添加元素到结构中,返回添加完的Set对象
new Set().add(2); //Set结构中存在2
# Set.protoype.delete(value) 从结构中删除元素,返回是否删除成功的布尔值
let set = new Set().add(3);
set.delete(3); //true
set.delete(4); //false
# Set.prototype.has(value) 检测某个值是否存在于Set结构中,返回是否存在的布尔值
let set  = new Set().add(4);
set.has(4); //true
set.has(5); //false
# Set.prototype.clear(value) 清空所有在Set结构中的元素
let set = new Set();
[1,2,3,4,5].forEach(v=>set.add(v));
set; //存在元素1,2,3,4,5
set.clear();
set; //清空为元素为空
# 常用的四个遍历方法
llet set = new Set(['red', 'green', 'blue']);
# Set.prototype.keys():返回键名的遍历器
for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue
# Set.prototype.values():返回键值的遍历器 因为Set继承了Iterable接口,
# 所以也可以忽略 values()方法,直接从遍历中读出值
for (let item of set.values()) { // or for(let item of set){console.log(item);}
  console.log(item);
}
// red
// green
// blue
# Set.prototype.entries():返回键值对的遍历器
for (let item of set.entries()) {
  console.log(item);
}
//这里需要注意,Set 结构的键名就是键值(两者是同一个值),因此第一个参数与第二个参数的值永远都是一样的。
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
# Set.prototype.forEach():使用回调函数遍历每个成员
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
# 当然拓展运算符也可以用于Set数据结构中

WeakSet

WeakSet和Set结构同一是一个无重复的值的结构。常用的实例属性和方法也类似。

WeakSet和Set有两个区别:

WeakSet存放的成员只能是对象,不能存放其他类型的值。

let wk = new WeakSet();
wk.add(1); //类型错误
wk.add({});//能够接受

WeakSet 中的对象都是弱引用的。

WeakSet的基本用法

语法如下:

# 创建:通过构造函数构造WeakSet实例
const ws = new WeakSet();
# 赋值:通过参数赋值,参数接受数组或者其他继承iterable接口的数据类型的 “对象” 。
# 注意点
# 须注意的是 WeakSet() 结构内的成员必须为对象。
const ws1 = new WeakSet(1); //TypeError 不接受非对象参数
const ws2 - new WeakSet('1'); //TypeError 不接受非对象参数
const ws3 = new WeakSet([1,2,3]);TypeError 不接受非对象参数
const ws4 = new WeakSet([[1],[2]]); //WeakSet {[1],[2]}
# 常用方法 没有size方法因为其成员都是不可遍历的
const ws  = new WeakSet([[1]]);
# WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
ws.add([2]);
ws;// WeakSet{[1],[2]}
# WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
ws.delete([2]);
ws;// WeakSet{[1]}
# WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中
ws.has([1]); // true
ws.has([2]); //false

新的数据结构 Map

基本用法

抛出概念:为什么ES6中要提出Map数据结构?

答:在JavaScript中Object对象本质上是一个键值对的集合(Hash)结构。但是传统上只能用字符串作为键名,这对它的使用有很大的限制。

const object = {};
# 假设原意是将一个 节点对象作为键名
const element = document.getElementById('myDiv');
//element=[object HTMLDivElement]
object[element] = 'data';
# 以节点对象作为键名调用数据,则会发生错误
# 原因是在ES6之前会自己将键名(不管是否为对象)自动转换为字符串
object[[object HTMLDivElement]];//undefinde
# 键名以字符串表达才能正常调用数据
object['[object HTMLDivElement]'];//data

为了解决上述键名必须以字符串表达的形式的问题,ES6推出了Map数据结构

Map数据结构也是一种键值对的集合和Object一样。区别在于Map数据类型的键名表达不仅限于字符串表达。Map数据结构中键名的表达还可以是各种类型对象或值的表达。

如果你需要“键值对”的数据结构,Map 比 Object 更合适。

# 承上:对照代码段
const map = new Map();
# 假设同样也是将一个 节点对象作为键名
const element = document.getElementById('myDiv');
map.set(element,'value');
map.get(element); //'value'
//读取到value的原因是map直接把element的变量名作为键名,而非解析变量名里的内容作为键名。

具体语法如下:

# 创建:通过构造函数创建Map实例
const map = new Map( );
# 参数说明
# Map()构造函数接受一个数组作为参数。数组成员以一对对表示键与值数组[key,value]表示
const map = new Map(
[ //ps:这里是合法的赋值,JavaScript的数组接受不同数据类型的值
['name','jack'],
['age',18]
]
)
map;//map{'name':'jack','age':18}

Map的实例属性和方法

const map = new Map(['age',20]);
# Map的属性
# size 属性 获取Map数据结构的键值对个数
map.size();//1
# Map操作方法
# 添加键值对:Map.prototype.set(key, value)
map.set('sex','male');
map.set('name','jack');//jack
# 获取对应键的值:Map.prototype.get(key)
# os:找的是对应的key,找不到返回undefind
map.get('name');//jack
map.get('age');//18
map.get('sex');//male
# 检查结构中是否存在某个Key:Map.prototype.has(key) 返回布尔值
map.has('name') ;//true
map.has('bahevior');//false
# 删除结构中对应键与值:Map.prototype.delete(key) 返回布尔值
map.delete('name');//true
map.delete('bahevior');//false
# 遍历方法
# Map.prototype.keys():返回键名的遍历器。
# Map.prototype.values():返回键值的遍历器。
# Map.prototype.entries():返回所有成员的遍历器。
# Map.prototype.forEach():遍历 Map 的所有成员。
# 例子跟Set结构差不多,比较简单,就不作介绍了。

Map 与其他数据结构的互相转换

语法如下:

const map = new Map([
['name','jack'],
['age',18],
['sex','male']
])
# 1.Map 转为数组
var arr = [...map];
# 2.数组转为Map
var arr = [
['name','jack'],
['age',18],
['sex','male']
];
//将以键值对的数组成员的数组传入到map构造函数中
//ps:如果所有 Map 的键都是字符串,它可以无损地转为对象
//ps:如果为非字符串,会先将值读取后再转为字符串以作为键
const map = new Map(arr);
# 3.Map转为对象
//自定义转换函数
function change(map){
let objcet  = Object.create(null);
for(let [key,value] of map.entries()){
object[key] = value;
}
return object;
}
change(map);//Object {key,value....}
# 4.对象转为Map
let element = {
'name':'jack',
'age':18,
'sex':'male'
};
//使用ES6新拓展的Object方法
# Object.entries( ) 以对象中的能遍历对象以键值对进行遍历
new Map(Object.entries(element));
# 5.Map转为JSON
# Map 转为 JSON 要区分以下两种情况
# 当Map键名都是字符串时,使用JSON.stringify()将Js的值转为JSON
//因为Map的成员对象都是以键值对表示的数组
//所以可以通过递归的方式一组一组地将Map成员对象转为Json
function change(map){
return JSON.stringify(change(map));
}
# 当Map的键名为非字符串时
# 选择性地将其转换为数组JSON
function mapToArrayJson(map) {
  //转换为以逗号分隔的参数序列后复制成为数组
  return JSON.stringify([...map]);
}
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
# 6.JSON转为Map
# 一般来说,JSON转为对象,即为从JSON中使用JSON.parse读出JSON串后
# 以new Map(Object.entries(JSON串))即可转换为map对象

WeakMap

WeakMap与Map的结构基本类似。
 

const map = new Map([
['name','jack'],
['age',18],
['sex','male']
]);
const wkmap = new WeakMap([
['name','jack'],
['age',18],
['sex','male']
])

主要的区别有以下两点:

1.WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。键名为弱引用,值是正常使用的。

const wkm = new WeakMap();
wkm.set(1, 2)
// TypeError: 1 is not an object!
wkm.set(Symbol(), 2)
// TypeError: Invalid value used as weak map key
wkm.set(null, 2)

2.WeakMap的键名所指向的对象,不计入垃圾回收机制。

即WeakMap对象 不再被引用 或 引用被清除后,WeakMap对象也会被垃圾回收机制自动回收。释放该对象所占的内存空间。而无须手动置对象为null。

总之,WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。

代理对象 Proxy

Proxy对象

Proxy用于修改某些操作的默认行为,等同于在语言层面作出修改,通过Proxy去编程的方式又称为元编程。

Proxy对象实际上是一个 “代理” 对象。用于在拦截、和限制外界对它所定义的被拦截对象的访问。

语法如下:

# ps:本身Proxy对象也可以作为其他对象的原型
# 创建:new Proxy(target,handler)
const proxy = new Proxy({},{});
# 参数说明
# target参数为要被代理的对象。当该对象为空时,默认被代理的对象即为其声明本身的属性对象。
# handler参数为被代理对象的处理方法,handler触发为当target被拦截后触发
# 不声明代理目标
let obj = new Proxy({},{ //target为空,拦截对象即为本身obj
//下面会逐步介绍该handler参数里的方法
get:function(target,propKey){
return 35;
},
set:function(target,propKey){
code......
 }
})
obj.age; //此处调用虽然没有这个变量但是get方法拦截在调用变量之前,所以被拦截后返回35
# 声明代理目标
let el = {
'name':'jack',
'age':18,
'sex':'male'
}
# 定义proxy对象,并添加代理目标
let proxy = new Proxy(el,{
//target为拦截目标,propKey为目标的属性
get:function(target,propKey){
if(propKey==='name'){
//拦截该属性并赋值为tom
return 'Lili';
}
if(propKey==='age'){
return 19;
}
if(propKey==='sex'){
return 'famle';
}
}
})
# 通过proxy访问被拦截的对象
proxy.name;//Lili
proxy.age;//19
proxy.sex;//famle

Proxy实例常用的方法

此处的实例方法,即为处于Handler参数体的方法。

let proxy = new Proxy({目标对象},{.....Proxy的实例方法,用于处理目标对象});

get( )

该方法用于拦截目标对象对某个属性的读取操作。

语法如下:

# get( )方法
# 参数说明 get(target,propKey,receiver)
# target 参数为目标对象
# propKey 参数目标对象的属性
# receiver 为可选参数 它代表为proxy实例本身
let name = '张三';
let person = {name};
let proxy = new Proxy(person,{
//ps:方法也可以写成es6格式 set() 省略function关键字
get:function(target,propKey,receiver){
if(propKey in target){
return target[propKey];
}else{
throw new ReferenceError();
}
}
});
proxy.name; //张三
proxy.age;//抛出引用错误
# 继承proxy
let obj = Object.create(proxy);
obj.name;//张三

set( )

该方法用于拦截目标对象对某个属性的赋值操作。

语法如下:

# 参数说明 set(target,prop,value,receiver)
# target 参数为目标对象
# prop 参数为目标对象的属性
# value 参数为目标对象的属性值
# receiver 为可选参数 它代表为proxy实例本身
let age = 18;
let person = {age};
let proxy = new Proxy(person,{
//ps:方法也可以写成es6格式 set() 省略function关键字
set:function(target,prop,value,receiver){
if(prop in target){
if(prop==='age'){
if(value>20){
throw new ReferenceError();
}else{
return target[prop] = value;
}
}
}else{
throw new ReferenceError();
}
},
get:function(target,propKey,receiver){
if(propKey in target){
return target[propKey];
}else{
//抛出引用错误
throw new ReferenceError();
}
}
});
proxy.age = 18;
proxy.age;//18
proxy.age = 21;
proxy.age;//抛出引用错误
# ps:以上也可以拓展为限制私有变量的访问。如果碰到私有变量则抛出异常

apply( )

该方法用于拦截函数的调用、call( )和apply( )操作。

语法如下:

# 参数说明 apply(target,ctx,args)
# target 参数为目标对象
# ctx 参数为目标对象的上下文对象
# agrs参数为目标对象的参数序列
let target = function () {
  return "I am the target";
};
let obj = new Proxy(target, {
  //ps:方法也可以写成es6格式 apply() 省略function关键字
  apply: function (target, ctx, args) {
    return "调用函数被拦截";
  }
});
obj();//调用函数被拦截

has( )

该方法用于拦截的HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。

语法如下:

# 参数说明 has(target,key)
# target 参数为目标对象
# key 参数为查询目标对象的属性名
let _name = 'jack';
let person = {_name};
let proxy = new Proxy(person,{
    has:function(target,key) {
      if (key[0]==='_') {
        return true;
      }else{
        return false;
      }
    }
});
//使用 in 操作符 触发了Proxy对HasProperty的拦截,从而触发has( )方法
if('_name' in proxy){
  console.log('存在该私有属性');
}else{
  console.log('不存在该私有属性');
}

Proxy.revocable( )

该对象方法用于返回一个可撤回的Proxy对象。

# 参数说明 Proxy,revocable(target,handler)
# target 参数为目标对象
# handler 参数为处理目标对象的函数
# 返回值说明
# 一、proxy属性为proxy实例
# 二、revoke属性为一个revoke函数,可用于回收代理权,当执行revoke函数后,proxy再也不可用。
let target = {};
let handler ={};
let {proxy,revoke} = Proxy.revocable(target,handler);
proxy.age = 19;
console.log(proxy.age); //19
revoke();
console.log(proxy.age); //调用revoke函数后,代理不可用,抛出异常
Proxy.revocable()的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就调用revoke( )函数收回代理权,不允许再次访问。

Promise对象

含义

Promise为ES6提出的实现异步请求的对象。其特点如下两点:

(1)Promise对象状态不受外界影响。Promise对象有三种状态,分别为

pending(等待态)、fulfilled(成功态)、和rejected(失败态)。只有Promise对象在进行异步操作时才能决定该状态。其他均不能影响Promise当前的状态。

(2)Promise状态一旦改变后,就处于不可变状态。 Promise状态的转变只有两种一是从pending(等待态)到fulfilled(成功态),第二为从pending(等待态)到rejected(失败态),一旦发生状态转变后,以后均是这个转变状态,即该状态已定型(resolved),以后不能再发生改变。

假设在进行异步操作时,Promise的状态从pending到fulfilled,一旦错过,再去监听Promise对象,是得不到结果的。

Promise对象的优缺点如下:

优点:相比如ES6前,开启异步操作,常需要嵌套大量的函数,导致整个异步操作的结构体繁琐复杂。而Promise对象开启异步则可简洁异步操作。

缺点:Promise对象一旦创建后就会立即执行且不能中途取消。其次,如果不设置回调函数,Promise内部的异常则不会抛出到外部。(回调函数的设置具体看下面语法)

Ps: 如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。

基本用法

语法如下:

# 一、 创建:使用构造函数创建实例
let promise = new Promise();
# 二、参数说明
# new Promise(function(resolve, reject){code.....});
# Promise构造函数接受一个函数作为参数
# 该函数的两个参数resolve和reject为JavaScript部署,不用手动配置。表现实际为两个函数
# resolve() 为 Promise状态从pending到resolved时调用,即异步操作成功后调用。
# 并将异步操作成功结果,通过 resolve(value)中传出
# rejectd() 为Promise状态从pending状态到rejected时调用,即异步操作失败后调用。
# 并将异步操作失败的结果,通过 rejecet(error)中传出
代码段如下:
let promise = new Promise(function(resolve,reject){
if(/*异步操作成功*/){
//该value传入回调函数中
resolve(value);
}else{
//该error传入回调函数
reject(error);
}
})

实例方法 then( )

Promise对象的回调函数 then( )

语法如下:

# Promise对象的回调函数
# Promise对象的实例生成后,可以通过 then()方法创建回调函数
# 参数说明 then(function(value),...function(error))
# 通过 then()函数可以分别指定处理resolve状态的回调函数和reject状态的回调函数
# 注意:then() 为异步方法体
代码段如下:
let promise = new Promise(function (resolve, reject) {
  //promise对象一旦创建后就会立马触发
  //......同步方法体
  console.log(" Promise对象同步方法体");
  setTimeout(() => {
    console.log("在Promise对象同步方法体中异步方法体");
  }, 3000);
  //调用resolve的回调函数
  resolve('异步');
});
promise.then(function (value) {
//异步方法体
   console.log('Promise对象'+value+"方法体的回调函数");
});
console.log("全文同步方法体");
输出顺序为:
Promise对象同步方法体 =》
全文同步方法体 =》
Promise对象异步方法体的回调函数=》
在Promise对象同步方法体中异步方法体

PS:Promsise对象回调函数的优先级高于Promise对象同步方法体中开启的异步函数。

实例方法 catch( )

用于指定Promise对象中任何异常错误的回调函数。

语法如下:

# 创建 catch()
let promise = new Promise(function(){});
promise.then(function(){})
.catch(function(){..捕捉整个Promise对象的异常..})

实例方法 finally( )

不管Promise状态如何发生变化,最终都会执行的函数finally( )

语法如下:

# 创建
let promise = new Promise(function(){});
promise.then(function(){})
.catch(function(error){..捕捉整个Promise对象的异常..})
.finally(function(){...最终执行的代码段})
# Ps:finally()方法的函数不接受任何的参数

对象方法 all( )

方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

# 一、创建
let promiseAll  = Promise.all([xx,xx,xx]);
# 二、参数说明
# Promise.all([p1,p2,p3])
# all方法可以接受多个参数,但每个参数都必须是一个Promise对象
# 如果其中有一个不为Promise对象,即会调用Promise.resolve转换为Promise对象
# 参数一般为成员以promise为对象组成的数组
# 也可不为数组,但其参数必须是iterable接口的数据结构和其成员必须是promise对象
代码片段如下:
let promiseAll = Promise.all([p1,p2,p3])
# 三、本身使用Promise.all为一个组合的Prosmise实例
# 它可以拥有 then()、catch()、finally( )等方法
# let promiseAll = Promise.all([p1,p2,p3]).then().catch().finally()
# 四、实例方法执行规则
# 其由Promsise.all()创建出来的Promise对象的实例方法执行规则如下:
let p = Promise.all([p1,p2,p3]).then().catch().finally()
# Promise.all().then() 方法
第一种情况:当p1,p2,p3的状态均达fulfilled后,p的状态才为fulfilled。
此时将返回一个由p1,p2,p3返回值组成的数组,到p的 then() 回调函数上。
第二种情况:当p1....p3只要有一个对象为rejected状态,p的状态就变为rejected,
此时p1....p3中第一个状态为reject的对象,其返回值将传入到 then( )中
注意:某一个实例状态发生错误 但 “能被其自身catch( )及时处理” 时,其状态不会处理成reject。
其状态也会被处理成resolve。
第三种情况:当p1....p3其中一个为实例发生reject错误时,如果能被及时处理成定型resolve状态,
此时将同样地返回一个由p1,p2,p3返回值组成的数组,到p的 then() 的回调函数上。
第三种情况 举例说明:
const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(resolve => resolve)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(resolve => resolve)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result)) //['hello world',Error]
.catch(e => console.log(e));
上面代码中:p1的状态从 pedding 到 fulfilled,最终resolve(定型)。返回值为回调函数的结果。
           p2的状态从 pedding 到 rejected,但由于被其自身的 catch() 方法及时处理错误,最终也为成功的resolve(定型)状态。
           返回值为catch()函数返回的结果。       
           最后将p1,p2放入Promise.all( )中组成混合实例。由于p1,p2均为resolve状态,所以调用其自身的 then() 函数返回,以P1,P2返回值作为成员的数组。
           ['hello world',Error]
# Promise.all().then().catch()
catch()用于捕抓Promise.all()的异常。例子如上。
须十分注意的是,如果Promise.all( )的实例上,已经定义了catch()方法。
如上述p1,p2.则本身的Promise.all().then().catch()就不会再次调用.

对象方法 race( )

Promise.race( )方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

与Promise.all( )方法不同的是,Promise.race( ) 方法参数中一个实例的状态发生变化,自身就率先跟着发生同样的状态变化。参数率先改变状态实例返回值传入到Promise.race( ).then(xxx) 中。

# p1和p2的初始状态都为pedding
# 第一种情况:假设p1在3秒内请求数据成功,状态从pedding到fulfilled,最终resolve。
# 即p2处于pedding时,p1的状态率先发生改变。p也跟着改变,其p1的返回值会传入到p的回调函数 then() 中
# 第二种情况:假设p1在3秒内没有请求数据成功,p2定时器成功调用完毕
# p2状态从pedding到rejected,最终resolve。则p也会跟随p2的状态。
# 由于p2没有定义自身的catch()处理函数,最终将p2返回结果Error对象传到Promise.race().then().catch()上。由catch()进行处理。
let p1 = new Promise(function (resolve, reject) {
  resolve("p1请求数据");
});
p1.then((resolve) => resolve);
let p2 = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error('请求超时')), 3000);
});
p2.then((resolve) => resolve);
let p = Promise.race([p1, p2]);
p.then(result => console.log(result)); //第一种情况:p1请求数据
p.catch(result=>console.log(result); //第二种情况:Error:请求超时

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值