ES6
声明变量
var
function
let
- 块级作用域。
- 不存在变量提升。
- 同一作用域内不可重复声明。
const
- 块级作用域。
- 一定要赋初始值。
- 值不可修改。
但是对于数组和对象的元素进行修改,不会报错。因为数组和对象的常量中保存的是对它们的引用地址,值发生改变不会影响引用地址。
const TEAM = [‘A’, ‘B’, ‘C’];
import
class
注意
- ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
var
命令和function
命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。
解构赋值
解构赋值指允许从数组和对象中提取值,对变量进行赋值。
//数组
//数组的元素是按次序排列的,变量的取值由它的位置决定。
const F4 = ['小沈阳', '刘能', '赵四', '宋小宝'];
let [xiao, liu, zhao, song] = F4;
console.log(xiao);//小沈阳
//对象
const zhao = {
name: '赵本山',
age: '不详',
xiaopin: function(){
console.log("aa");
}
};
//完整写法
//属性名:变量名
let {name: name, age: age, xiaopin: xiaopin } = zhao;
//如果变量名和属性名相同,可以简写成
let {name, age, xiaopin} = zhao;
//对象的解构赋值可以取到继承的属性。
const obj1 = {};
const obj2 = {foo: 'bar'};
Object.setPrototypeof(obj1, obj2);
const {foo} = obj1;
foo // 'bar'
//字符串
const [a, b, c, d, e] = 'hello';
a //'h'
let {length: len} = 'hello';
len //5
//数值和布尔值
//函数参数
//可以指定默认值。使用===判断一个位置是否有值,只有严格等于undefined,默认值才会生效。
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
//用途
//1、交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
//2、提取数据
let data = {
id: 42,
status: 'ok',
data: [214, 321]
};
let {id, status, data: number} = data;
console.log(number); // [214, 321]
//3、设置函数参数的默认值
function(url, {async = true, global = true }={}){
...
}
//、引入模块的制定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
字符串
//模版字符串`
let str = `呃呃呃呃呃呃`;
//允许直接出现换行符,所有的空格和缩进都会被保留。
let html = `<div>
<span></span>
</div>`;
//变量拼接
let html = `<div>
<span>${str}</span>
</div>`;
//includes()
//返回是否找到了参数字符串。第二个参数表示开始搜索的位置。
let s = 'hello world!';
s.includes('l') // true
s.includes('hello', 6) // false
//startsWith()
//返回参数字符串是否在原字符串的头部。第二个参数表示开始搜索的位置。
s.startsWith('h') // true
s.startsWith('world', 6) // true
//endsWith()
//返回参数字符串是否在原字符串的尾部。第二个参数表示针对前n个字符。
s.endsWith('!') // true
s.endsWith('hello', 5) // true
//repeat()
//返回一个新的字符串,表示将原字符串重复n次。
'x'.repeat(3) // 'xxx'
//参数是小数,会向下取整。
'na'.repeat(2.9) // 'nana'
//参数是负数或Infinity,会报错。
'na'.repeat(Infinity) // RangeError
'na'.repeat(-1) // RangeError
//参数是0~-1之间的小数,则取0。
'na'.repeat(-0.9) // ''
//参数是NaN,则取0。
'na'.repeat(NaN) // ''
//参数是字符串,会先转换成数字。
'na'.repeat('na') // ''
'na'.repeat('3') // 'nanana'
//at()
//返回参数指定位置的字符,支持负索引(即倒数的位置)。
const str = 'hello';
str.at(1) // 'e'
str.at(-1) // 'o'
数值
//Number对象
//Number.EPSILON是JS表示的最小精度。主要用于浮点数。
//接近于2.22044...
function equal(a, b){
if(Math.abs(a-b) < Number.EPSILON){
return ture;
}else{
return false;
}
}
console.log(0.1 + 0.2 === 0.3);//false
console.log(equal(0.1+0.2, 0.3));//true
//二进制、八进制
let b = 0b1010;
let o = 0o777;
//Number.isFinite()
//检查是否有限。
isFinite(25) // true
isFinite('25') // true
Number.isFinite(25) // true
Number.isFinite('25') // false
//Number.isNaN()
//检查是否为NaN。
//它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值再进行判断,而这两个新方法只对数值有效。
isNaN(NaN) // true
isNaN('NaN') //true
Number.isNaN(NaN) //true
Number.isNaN("NaN") // false
Number.isNaN(1) // false
//Number.parseInt()
//Number.parseFloat()
//将全局方法`parseInt()`和`parseFloat()`,移植到`Number`对象上面,行为完全保持不变。
//Number.isInteger()
//判断一个数值是否为整数。
Number.isInteger(25) // true
Number.isInteger(25.1) // false
//存在误判
Number.isInteger(25.0) // true
//如果一个数值的绝对值小于`Number.MIN_VALUE`(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0。这时,`Number.isInteger`也会误判。
Number.isInteger(5E-324) // false
Number.isInteger(5E-325) // true
//Math对象
//Math.trunc()
//去除一个数的小数部分,返回整数部分。
Math.trunc(4.1) //4
//对于非数值,会先转为数值再处理。
Math.trunc(null) //0
//对于空值和无法截取整数的值,返回NaN。
Math.trunc() //NaN
//Math.sign()
//参数为正数,返回+1;参数为负数,返回-1;参数为0,返回0;参数为-0,返回-0;其他,返回NaN。
//对于非数值,会先转为数值再处理。
//Math.cbrt()
//计算一个数的立方根。
//Math.clz32()
//count leading zero bits in 32-bit binary representation of a number
//将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。
//对于小数,只考虑整数部分。
//对于非数值,会先转为数值再处理。
//Math.imul()
//返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
//Math.fround()
//返回一个数的32位单精度浮点数形式。
//Math.hypot()
//返回所有参数的平方和的平方根。
//Math.expm1(x)
//返回e^x-1。
//Math.log1p(x)
//返回log(1+x)。
//Math.log10(x)
//返回log10(x)
//Math.log2(x)
//Math.sinh(x)
//Math.cosh(x)
//Math.tanh(x)
//Math.asinh(x)
//Math.acosh(x)
//Math.atanh(x)
函数
(1)箭头函数
箭头函数适合与this无关的回调,比如定时器、数组的方法回调。
不适合与this有关的回调,比如事件回调、对象的方法。
let f = (a, b) => {};
//等同于
let f = function(a, b){};
//箭头函数的简写。
//1、省略(),只有一个形参。
let f = a => {};
//2、省略{},当代码体只有一条语句时,此时return必须省略。函数执行结果就是返回值。
let f = n => n*n;
//3、返回对象要在对象外加括号,否则会被认为是代码块。
let getItem = id => ({id: id, name: "temp"});
//不能作为构造实例化对象。
//构造函数
function Person(name, age){
this.name = name;
this.age = age;
}
//错误:用箭头函数构造实例化对象
let Person = (name, age)=>{
this.name = name;
this.age = age;
}
//没有自己的this对象。
//对于普通函数来说,内部的`this`指向函数运行时所在的对象,箭头函数内部的`this`就是定义时上层作用域中的`this`。
function getName(){
console.log(this.name);
}
let getName2 = ()=>{
console.log(this.name);
};
window.name = 'window';
const school = {
name: 'school'
};
getName.call(school);//'school'
getName2.call(school);//'window'
//不能使用arguments对象。如果要使用,可以使用rest参数代替。
//不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
(2)参数
函数参数的默认值
//一般具有默认值的参数位置靠后。
//参数是按顺序赋值的,如果c插在中间即add(a, c=10, b),2会赋值给c,则b=undefined。
function add(a, b, c=10){
return a+b+c;
}
let result = add(1, 2);
//与解构赋值结合
function connect({host = '127.0.0.1', username, password, port}){
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
connect({
username: 'root',
password: 'root',
port: 3306
});
rest参数
- 形式为…变量名
- 用于获取函数的实参,用来代替arguments。
- 必须放在参数最后。
- 返回的是数组。
function date(a, b, ...args){
console.log(a);
console.log(b);
console.log(args);
}
data(1, 2, 3, 4,5);
//a = 1, b = 2, args = [3, 4, 5];
(3)属性方法
函数的length属性
//指定了默认值后,length=没有指定默认值的参数个数。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
//不计入rest参数。
(function (...args){}).length // 0
//不计入设置了默认值的参数后面的参数个数。
(function (a = 0, b, c){}).length // 0
(function f(name, age=18, gender, ...args){}).length // 1
//面试题
123['toString'].length + 123//124
//123相当于new Number()
//顺着隐式原型链,new Number()=>Number.prototype,找到toString属性(方法是特殊的属性),toString.length=1
函数的name属性
- 返回该函数的函数名。
//如果将一个匿名函数赋值给一个变量,ES5会返回空字符串,而ES6会返回变量名。
var f = function(){};
//ES5
f.name // ""
//ES6
f.name // "f"
//Function构造函数返回的函数实例,返回anonymous。
(new Function).name // "anonymous"
//bind返回的函数,会加上bound前缀。
function foo(){};
foo.bind({}).name //"bound foo"
数组
(1)扩展运算符
扩展运算符…
- 能将数组转化为用逗号隔开的参数序列。
const strs = ['a', 'b', 'c'];
function f(){
console.log(arguments);
}
f(...strs);
//等同于
f('a', 'b', 'c');
//应用
//1、数组合并(浅拷贝)
//以前:因为push()参数不能是数组,需要利用apply方法。
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
//ES6
arr1.push(...arr2);
//2、数组的克隆(深拷贝)
const str1 = ['a', 'b'];
const str2 = [...str1];
//3、将伪数组转为真正的数组
//可以实现将arguments转为真正的数组。
const divs = document.querySelectorAll('div');
const divArr = [...divs];
//4、将数组内的成员作为函数的参数
//以前:利用apply方法。
f.apply(null, args);
//ES6
f(...args);
(2)属性方法
//Array对象自身的方法
//Array.from():可以将类似数组的对象、可遍历的对象(包括Set和Map)转为真正的数组。
var arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
//ES5
var arr1 = [].slice.call(arrayLike);// ['a', 'b', 'c']
//ES6
let arr2 = Array.from(arrayLike);// ['a', 'b', 'c']
//还可以接受第二个参数,作用类似于数组的`map`方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
Array.from(arrayLike, x => x * x);
//等同于
Array.from(arrayLike).map(x => x * x);
//Array.of():将一组值,转为数组。规避Array()的诡异行为。
Array.of() // []
Array.of(3) // [3]
Array.of(3, 11, 8) //[3, 11, 8]
//Array()
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]
//Array原型对象上的方法
//copyWithin(target, start, end):在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。使用这个方法,会修改当前数组。
/*
target(必要):从该位置开始替换数据。可负数。
start:从该位置开始读取,默认为0。
end:从该位置前停止读取,默认为数组长度。
*/
[1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]
//find():返回第一个符合条件的数组成员。
//find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
[1, 2, 3, 4, 5].find((value, index, arr)=>{
return value > 3;
}) // 4
//find方法还可以接受第二个参数,绑定回调函数的this对象。
//findIndex():返回第一个符合条件的数组成员下标。
//以上方法都可以发现NaN。
//fill(value, start, end):按照value填充[start, end)数组。
/*
value(必要):用来初始化的值。
start:开始位置,默认为0。
end:结束位置, 默认为数组长度。
*/
//entries():对键值对的遍历。返回的是Iterator对象。
for(let [index, elem] of ['a', 'b'].entries()){
console.log(index, elem);
}
//keys():对键名的遍历。返回的是Iterator对象。
for(let index of ['a', 'b'].keys()){
console.log(index);
}
//values():对键值的遍历。返回的是Iterator对象。
for(let elem of ['a', 'b'].values()){
console.log(elem);
}
//at():接受一个整数作为参数,返回对应位置的成员,支持负索引。这个方法不仅可用于数组,也可用于字符串和类型数组(TypedArray)。
//如果参数位置超出了数组范围,则返回undefined。
const arr = [5, 12, 8, 130];
arr.at(2) // 8
arr.at(-2) // 8
(3)数组的空位
数组的空位指的是,数组的某一位置上没有任何值。
- 比如Array(3) // [, , ,]
ES5
forEach()
,filter()
,reduce()
,every()
和some()
都会跳过空位。map()
会跳过空位,但会保留这个值join()
和toString()
会将空位视为undefined
,而undefined
和null
会被处理成空字符串。
ES6
- 将空位转为undefined。
对象
//对象的简写
//允许在大括号里直接写入变量和函数,作为对象的属性和方法。
//同名变量简写。
let name = 'a';
const obj = {
name,
//等同于
//name: name
improve(){}
//等同于
//improve: function(){}
};
//对象的定义(字面量)
//ES5:属性名不能是表达式。
var obj = {
foo: true,
abc: 123
};
//ES6:属性名可以是表达式、变量,要用方括号扩起来。
let lastWord = 'last_word';
const a = {
'first_word': 'hellp',
[lastWord]: 'world',
['e'+'nd']: '!'
}
//遍历对象的属性
//for...in:遍历对象自身的、继承的可枚举属性(不含Symbol属性)
//Object.keys(obj):返回数组,包括对象自身的可枚举属性的键名(不含Symbol属性)。
//Object.getOwnPropertyNames(obj):返回数组,包括对象自身的所有属性的键名(不含Symbol属性)。
//Object.getOwnPropertySymbols(obj):返回数组,包括对象自身的所有Symbol属性的键名。
//Reflect.ownKeys(obj):返回数组,包括对象自身的所有属性的键名。
//对象的方法
//Object.is():判断两个值是否相同。
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
//==:自动转换数据类型
//===:NaN不等于自身;+0等于-0
//Object.assign():将源对象的所有可枚举属性复制到目标对象。返回目标对象。(拷贝源对象自身的可枚举属性,包括Symbol属性;浅拷贝)
//第一个参数是目标对象,后面的参数都是源对象。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);// { a: 1, b: 4, c: 5 }
console.log(returnedTarget);// { a: 1, b: 4, c: 5 }
//可以用来处理数组,但会把数组看作对象。
Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3]
//只能复制值,如果要复制的值是一个取值函数,那么将求值后再复制。
//Object.setPrototypeOf(target, prototype):设置原型对象。
//Object.getPrototyoeOf(target):获取原型对象。
super关键字
- 指向当前对象的原型对象
//super.foo等同于Object.getPrototypeOf(this).foo或Object.getPrototypeOf(this).foo.call(this)
const proto = {
foo: 'hello'
};
const obj = {
foo: 'world',
find(){
return super.foo;
}
};
Object.setPrototypeOf(obj, proto);
obj.find()// "hello"
//只能用在对象的方法中,用在其他地方都会报错。目前只有对象方法的简写法可以让JS引擎确定定义的是对象的方法。
//error: super用在属性里面
const obj = {
foo: super.foo
};
//error: super用在一个函数里,再赋值给foo属性。
const obj = {
foo: ()=> super.foo
};
const obj = {
foo: function(){
return super.foo;
}
};
__proto__
- 内部属性。
- 无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的
Object.setPrototypeOf()
(写操作)、Object.getPrototypeOf()
(读操作)、Object.create()
(生成操作)代替。
symbol
ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入
Symbol
的原因。
表示独一无二的值。基本上,它是一种类似于字符串的数据类型。
特点
- Symbol的值是唯一的,用于解决命名冲突的问题。
- Symbol定义的对象属性不能使用for…in、for…of循环遍历,也不会被
Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。但是可以使用Object.getOwnPropertySymbols()
来获取对象的所有Symbol属性名;或者使用Reflect.ownkeys
来获取对象的所有键名。
//创建symbol
let s = Symbol();
//Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述。
//主要是为了在控制台显示,或者转为字符串时,比较容易区分。
let s1 = Symbol('foo');
let s2 = Symbol('bar');
console.log(s1);//Symbol(foo);
console.log(s2);//Symbol(bar);
console.log(s1.toString());//'Symbol(foo)';
console.log(s2.toString());//'Symbol(bar)';
//如果SymbOl函数的参数是一个对象,则会调用该对象的toString方法,将其转为字符串,然后再生成一个Symbol值。
//Symbol函数的参数指示表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值也是不相等的。
let s1 = Symbol();
let s2 = Symbol();
s1===s2;//false
let s3 = Symbol('foo');
let s4 = Symbol('foo');
s3===s4;//false
//description属性可以直接返回Symbol的描述。
s3.description//"foo"
//Symbol值不能与其他类型的值进行运算,会报错。
"aaaa" + s;//TypeError: can't convert symbol to string
//Symbol值可以转为字符串、布尔值,但是不能转为数值。
String(s);//'Symbol()'
Boolean(s);//true
Number(s);//TypeError
//Symbol值作为对象属性名。
let s = Symbol();
//1
let a = {};
a[s] = 'hello';
//2
let a = {
[s]: 'hello'
};
//3
let a = {};
Object.defineProperty(a, s, {value: 'hello'});
a[s]//'hello'
//Symbol值作为对象属性名时,不能用点运算符。
//点运算符后面总是字符串。如果不将Symbol值放在方括号里,会将代表Symbol值的变量名作为字符串解析,从而键名就不是变量名所代表的Symbol值。
let s = Symbol();
let obj = {
[s]: function(arg){
}
};
//等同于
let obj = {
[s](arg){}
};
//有时,我们希望重新使用同一个 Symbol 值,Symbol.for()方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1===s2;//true
Symbol属性
//hasInstance
//当其他对象使用instanceof来判断是否为该对象的实例时,会调用该方法。
class Even{
static [Symbol.hasInstance](obj){
return Number(obj) % 2 === 0;
}
}
1 instanceof Even//false
//isConcatSpreadable
//布尔值,表示该对象用于Array.prototype.concat()是否可以展开。
//数组默认可以展开(undefined/true),对象默认不可以。
let arr2 = ['c', 'd'];
arr2[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']
iterator
const arr = ['a', 'b', 'c'];
//for...in遍历键名
for(let i in arr){
console.log(i);
}
//0
//1
//2
//for...of遍历键值
for(let i of arr){
console.log(i);
}
//'a'
//'b'
//'c'
Iterator
作用
- 为各种数据结构提供统一的访问接口。
- 使得数据结构的成员能够按照某种次序排列。
- 供for…of消费。当使用for…of遍历时,会自动寻找Iterator接口,默认的 Iterator 接口部署在数据结构的
Symbol.iterator
属性。遍历过程
- 创建一个指针对象,指向当前数据结构的起始位置。
let iterator = arrSymbol.iterator;- 第一次调用对象的next方法,指针自动指向数据结构的第一个成员。
iterator.next();- 接下来不断调用next方法,指针一直向后移动,直到指向最后一个成员。
- 每调用next方法返回一个包含value和done属性的对象。
原生具备Iterator接口的数据结构如下:
- Array
- Map
- Set
- String
- TypedArray
- arguments
- NodeList
//对象没有默认部署Iterator接口。
//手写iterator
const obj = {
data: [
'b',
'c',
'd'
],
[Symbol.iterator](){
let index = 0;
return {
next: ()=>{
if(index < this.data.length){
return {value: this.data[index++], done: false};
}else{
return {value: undefined, done: true};
}
}
}
}
}
//调用
for(let i of obj){
console.log(i);
}
//'b'
//'c'
//'d'
generator
//应用
//1、逐步获取数据
function getUsers(){
setTimeout(()=>{
let data = 'users';
//next()方法可以带一个参数,会被当作当前yield表达式的返回值。没有参数,则返回值是undefined。
iterator.next(data);
}, 1000);
}
function getOrders(){
setTimeout(()=>{
let data = 'orders';
iterator.next(data);
}, 1000);
}
function getGoods(){
setTimeout(()=>{
let data = 'goods';
iterator.next(data);
}, 1000);
}
function* gen(){
let users = yield getUsers();
let orders = yield getOrders();
let goods = yield getGoods();
}
//调用Generator函数后,并不执行,而是返回指向内部状态的指针对象。
let iterator = gen();
//第一次使用next(),传递参数是无效的。
iterator.next();
所谓"异步",简单说就是一个任务不是连续完成的,可以理解成该任务被人为分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。
比如,有一个任务是读取文件进行处理,任务的第一段是向操作系统发出请求,要求读取文件。然后,程序执行其他任务,等到操作系统返回文件,再接着执行任务的第二段(处理文件)。这种不连续的执行,就叫做异步。
相应地,连续的执行就叫做同步。由于是连续执行,不能插入其他任务,所以操作系统从硬盘读取文件的这段时间,程序只能干等着。
Set
Set
- 只有key,没有value。(key唯一)
//声明set
//可以接受数组(或者具有iterable接口的其他数据结构)作为参数。
let s = new Set([1,2,3,2,4]);
s // [1, 2, 3, 4]
//添加新的元素:返回Set结构本身。
//不会发生类型转换,判断两个值是否不同是通过类似于===,但认为NaN等于自身,两个对象总是不相等的。
s.add(1);
//删除元素:返回布尔值,表示是否删除成功。
s.delete(4);
//检测是否存在某个元素:返回布尔值,表示该元素是否为Set的成员。
console.log(s.has(4));// false
//元素个数
console.log(s.size);
//清除所有成员:没有返回值。
s.clear();
//遍历方法(按照插入顺序遍历)
//Set结构没有键名,只有键值(键名=键值)
Set.prototype.keys()
Set.prototype.values()
Set.prototype.entries()
Set.prototype.forEach()
//应用
//1、去重
//数组
let arr = [1,2,3,4,5,4,3,2,1];
let result = [...new Set(arr)];
//字符串
[...new set('abbbdas')].join('');//"abds"
//2、交集
let arr2 = [4,5,6,5,6];
let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
//3、并集
let union = [...new Set([...arr, ...arr2])];
//4、差集
let result = [...new Set(arr)].filter(item => !new Set(arr2).has(item));
WeakSet
- 与Set类似,但是成员只能是对象(可以是数组或类似数组的对象)。
- 对象都是弱引用(垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中)。
- 不可遍历。
//声明
let ws = new WeakSet();
//添加
const obj = {};
ws.add(obj);
//删除
ws.delete(obj);
//检查是否存在
ws.has(obj);
Map
Map
- 既有key也有value。
- 它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
- 也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
//声明
let m = new Map();
//添加
m.set('name', 'a');
m.set('change', function(){
...
});
let a = {
name: 'b'
};
m.set(a, [1, 2, 3]);
//大小
m.size // 3
//删除
m.delete('name');
//获取
m.get('change') // function(){...}
//检查
m.has('change') //true
//清空
m.clear();
//只有对同一个对象的引用,Map 结构才将其视为同一个键。Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。
const map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
//遍历方法(按照插入顺序)
Map.prototype.keys()
Map.prototype.values()
Map.prototype.entries()
Map.prototype.forEach()
WeakMap
- 类似Map,但是只接受对象(除了null)作为键名。
- 键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。
//声明
let wm = new WeakMap();
let key = {};
let obj = {};
//添加
wm.set(key, obj);
//删除
vm.delete(key);
//获取
vm.get(key)
vm.has(key)
//应用
//1、dom节点作为键名。
Proxy
代理器
- 在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
//声明
//target表示要拦截的目标对象,handler用来定制拦截行为。
var proxy = new Proxy(target, handler);
//get():拦截某个属性的读取操作。
var person = {
name: 'a'
};
var proxy = new Proxy(person, {
get: function(){
...
}
});
//set()
Reflect
Promise
Promise对象代表一个异步操作。
- 有三种状态:pending、fulfilled、rejected。状态只会改变一次且不可逆。
- 作用
解决回调地狱问题。
代码可读性提高。
//应用
//封装AJAX
const p = new Promise((resolve, reject)=>{
const xhr = new XMLHttpRequest();
xhr.open('GET', 'url');
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readystate === 4){
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
}
});
//then():第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数。
//then()方法的返回结果是promise对象。promise对象的状态由回调函数的执行结果决定。
p.then(value => {
//1、返回非promise类型的值,promise对象状态为fulfilled,值为该返回值。
return 123;
//2、返回promise类型的值,promise对象状态为该promise对象的状态,值为resolve/reject函数里的参数。
return new Promise((resolve, reject)=>{
resolve('ok');
});
//3、抛出错误,promise对象状态为rejected,值为Error的参数。
throw new Error('出错啦');
}, error => {
console.log(error);
...
});
//catch():rejected状态的回调函数。
p.catch(error=>{
})
Class
class Phone{
//构造方法:名字不能修改。
constructor(brand, price){
this.brand = brand;
this.price = price;
}
//必须使用函数式写法,不能使用对象写法
call(){
console.log('a');
}
//静态属性:不能被实例对象访问到,只能通过Phone.change()访问到。
static name = '手机';
static change(){
console.log('b');
}
//当通过实例对象读取该属性时触发,返回值就是函数的返回值。
get price(){
console.log('get');
return '1';
}
//当通过实例对象设置该属性时触发。
set price(value){
console.log('set');
}
}
let huawei = new Phone('华为', 4128);
huawei.call();//'a'
huawei.change();//undefined
console.log(huawei.price);
//'get'
//'1'
huawei.price = '2';
//'set'
class SmartPhone extends Phone{
constructor(brand, price, size, color){
super(brand, price);
this.size = size;
this.color = color;
}
//子类对父类继承的重写
call(){
...
}
}
模块化
好处
- 防止命名冲突。
- 代码复用。
- 高维护性。
ES6之前的模块化规范
- CommonJs=>NodeJS、Browserify
- AMD=>requireJS
- CMD=>seaJS
//export命令:规定模块的对外接口。
//1、分别暴露:直接在要暴露的变量前面加export。
//m1.js
export let s = 's';
export function f(){
...
}
//2、统一暴露
//m2.js
let s = 's';
function f(){
...
}
export {s, f};
//3、默认暴露
export default{
s: 's',
f: function(){
...
}
}
//import命令:输入其他模块提供的功能。
//1、在文件里直接引入
<script type="module">
//1、通用导入
import * as m1 from './src/js/m1.js';
console.log(m1.s);
import * as m2 from './src/js/m2.js';
console.log(m2.s);
import * as m3 from './src/js/m3.js';
console.log(m3.default.s);
//2、解构赋值
import {s, f} from './src/js/m1.js';
consoel.log(s);
import {s as s_plus, f as f_plus} from './src/js/m2.js';
console.log(s_plus);
import {default as m3} from './src/js/m3.js';
console.log(m3.s);
//3、简便:针对默认暴露
import m3 from "./src/js/m3.js";
console.log(m3.s);
</script>
//2、利用标签引入:用的ES6,兼容性不佳。
<script src="./src/js/app.js" type="module"></script>
//app.js
import * as m1 from './src/js/m1.js';
console.log(m1.s);
import * as m2 from './src/js/m2.js';
console.log(m2.s);
import * as m3 from './src/js/m3.js';
console.log(m3.default.s);
//导入npm包语法
import $ from 'jquery';
//3、babel转化后打包
<script src="dist/bundle.js"></script>
Babel
Babel是一个ES6转码器,可以将ES6代码转为ES5代码,从而在老版本的浏览器执行。
//.babelrc(run control)
{
//要被转换的
"presets": [
"@babel/env",
"@babel/preset-react"
],
"plugins": []
}
babel-cli:实现命令行转码工具。
babel-preset-env:实现转码。
browserify(webpack):打包工具。
ES7
数组
//Array原型对象的方法
//includes():返回数组是否包含给定的值。
/*
第一个参数(必要):给定值。
第二个参数:搜索的起始位置,默认为0。
*/
//可以查找NaN。
[1, 2, 3].includes(3, 1) //true
运算符
指数运算符**
2**4 //16
//运算时是右结合。
2 ** 3 ** 2// 相当于 2 ** (3 ** 2)
a **= 2 //相当于a = a * a
ES8
字符串
//字符串补全长度:如果某个字符串不够指定长度,会在头部或尾部补全。
//头部补全padStart()
'x'.padStart(5, 'ab') // 'ababx'
//尾部补全padEnd()
'x'.padEnd(5, 'ab') // 'xabab'
//原字符串长度+补全字符串长度>指定长度,则截去超出部分的补全字符串。
'x'.padEnd(5, '0123456') // 'x0123'
//如果省略第二个参数,默认使用空格补全。
'x'.padStart(5) // ' x'
对象
//Object.getOwnPropertyDescriptors():返回对象自身的所有属性的描述对象。
//Object.keys():返回对象自身的所有可遍历属性的键名。
//Object.values()
//Object.entries()
async函数
//async函数的返回值为promise对象。promise对象的结果由async函数执行的返回值决定(和then差不多)。
async function fn(){
return new Promise((resolve, reject)=>{
resolve('a');
});
}
const result = fn();
console.log(result);
await表达式
const p = new Promise((resolve, reject)=>{
resolve('success');
});
async function main(){
//await必须要放在async函数中。
//await右侧的表达式一般为promise对象。
//await返回的是promise成功的值。如果promise失败了,会抛出异常,需要通过try...catch捕获。
try{
let result = await p;
}catch(e){
console.log(e);
}
}
//应用
//async与await封装AJAX请求
function send(url){
return new Promise((resolve, reject)=>{
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readystate === 4){
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response);
}else{
reject(xhr.status);
}
}
}
})
}
async function main(){
let result = await send('url');
console.log(result);
}
main();
ES9
对象
//rest参数(浅拷贝)
let {x, y, ...z} = {x: 1, y: 2, a: 3, b: 4}
x // 1
y // 2
z // {a: 3, b: 4}
//解构赋值等号右边必须是一个对象。如果等号右边是undefined或null,就会报错,因为它们无法转为对象。
let { ...z } = null; //error
let { ...z } = undefined; //error
//rest参数必须是最后一个参数,否则会报错。
let {x, ...y, ...z } = obj; //error
//扩展运算符(对象自身的所有可遍历属性)
let a_copy = {...a};
//等同于
let a_copy = Object.assign({}, a);
正则
//命名捕获分组
//未使用时
let str = '<a href="http://www.baidu.com">abcd</a>';
const reg = /<a href="(.*)">(.*)<\/a>/;
console.log(reg.exec(str));
/*
[
'<a href="http://www.baidu.com">abcd</a>',
'http://www.baidu.com',
'abcd',
groups: undefined,
...........
]
*/
//使用后
const reg1 = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
console.log(reg1.exec(str));
/*
[
'<a href="http://www.baidu.com">abcd</a>',
'http://www.baidu.com',
'abcd',
groups: {
text: 'abcd',
url: 'http://www.baidu.com'
},
...........
]*/
//正向断言:根据后面的内容判断前面的内容。
//提取出啦啦啦前面的数字。
let str = 'js62349216你知道吗555啦啦啦';
const reg = /\d+(?=啦)/;
console.log(reg.exec(str));
//反向断言:根据前面的内容判断后面的内容。
const reg1 = /(?<=吗)\/d+/;
console.log(reg1.exec(str));
//dotAll模式
//.表示除换行符以外的任意单个字符。
ES10
字符串
//trimStart()
//返回原字符串消除头部的空格(tab、换行)后的新字符串。
//trimEnd()
const s = ' abc ';
s.trim() // 'abc'
s.trimStart() // 'abc '
s.trimEnd() // ' abc'
函数
toString()
- 以前toString()方法返回函数代码本身,但会省略注释和空格。现在会返回一模一样的原始代码。
try…catch
//以前明确要求`catch`命令后面必须跟参数,接受`try`代码块抛出的错误对象。
try{
...
}catch(err){
...
}
//现在允许`catch`语句省略参数。
try{
...
}catch{
...
}
数组
ES2019明确规定,
Array.prototype.sort()
的默认排序算法必须稳定。
//flat():将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。
[1, 2, [3, 4]].flat() // [1, 2, 3, 4]
//可以传入一个参数,表示要“拉平”的层数,默认为1。如果不管有多少层嵌套,都要转成一维数组,可以用`Infinity`关键字作为参数。
[1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]
[1, [2, [3]]].flat(Infinity) // [1, 2, 3]
//flatMap():先执行map(),再对返回值组成的数组执行flat()方法。返回一个新数组,不改变原数组。
//只能展开一层数组。
[2, 3, 4].flatMap(x => [x, x * 2]);
//[[2, 4], [3, 6], [4, 8]].flat()
//[2, 4, 3, 6, 4, 8]
对象
//Object.fromEntries():将一个键值对数组转为对象。
Object.fromEntries([
['foo', 'bar'],
['baz', 42]
])
// { foo: "bar", baz: 42 }
ES11
运算符
链运算符?.
//左侧的对象是否为null或undefined。如果是,则返回undefined;如果不是,则往下运算。
const firstName = message?.body?.user?.firstName || 'default';
a?.b //a == null ? undefined : a.b
a?.[x] //a == null ? undefined : a[x]
a?.b() //a == null ? undefined : a.b()
a?.() //a == null ? undefined : a()
Null判断运算符??
//左侧的值是否为null或undefined。如果是则返回右侧的值。
const animationDuration = response.settings?.animationDuration??300;
//先判断response.setting是不是null或undefined,是则返回undefined;不是则判断response.setting.animationDuration是不是null或undefined,是则返回300。
数值
新数据类型BigInt
- 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。
//BigInt类型的数据必须添加后缀n。
const a = 315264786478n;
//BigInt与普通整数是两种值,它们并不相等。
42n === 42 //false
//可以用typeof。
typeof 123n //'bigint'
//可以用负号,不能用正号。
-42n //正确
+42n //错误
//BigInt()
//将其他类型的值转为BigInt。
class
//私有属性#:无法在类外部读取。
class Person{
name;
#age;
constructor(name, age){
this.name = name;
this.#age = age;
}
intro(){
console.log(this.#age);
}
}
Promise
//allSettled():返回一个Promise对象。状态永远是resolved;值是传入的Promise数组返回的结果组成的数组。
console.log(Promise.allSettled([p1, p2]));
//all():返回一个Promise对象。传入的Promise数组都成功,状态是resolved值是传入的Promise数组返回的结果组成的数组;否则状态是rejected,值是失败的Promise返回的值。
console.log(Promise.all([p1, p2]));
globalThis
始终指向全局对象(不论是浏览器还是node环境下)。
模块化
//动态import
btn.onclick = function(){
import('./hello.js').then(module=>{
module.hello();
});
}
ES12
运算符
逻辑赋值运算符
//或赋值运算符
x ||= y // x || (x = y)
user.id = user.id || 1;
user.id ||= 1;
//与赋值运算符
x &&= y // x && (x = y)
//Null赋值运算符
x ??= y // x ?? (x = y)
字符串
//replaceAll()
'aabbcc'.replace('b', '_') // 'aa_bcc'
'aabbcc'.replaceAll('b', '_') // 'aa__cc'
//第一个参数,如果正则表达式是一个不带g修饰符的正则表达式,会报错。
'aabbcc'.replaceAll(/b/, '_') // error
//第二个参数,可以是以下特殊字符串。
//$&表示匹配字符串
'abbc'.replaceAll('b', '$&') // $&指`b`本身,结果是'abbc'
//$`表示匹配结果前面的文本
'abbc'.replaceAll('b', '$`') // 对于第一个b,$`指`a`;对于第二个b,$`指的是`ab`;结果是'aaabc'
//$'表示匹配结果后面的文本
'abbc'.replaceAll('b', `$'`) // 对于第一个b,$'指的是`bc`;对于第二个b,$'指的是`c`;结果是'abccc'
//$n表示匹配成功的第n组内容(第一个参数必须是正则表达式)
'abbc'.replaceAll(/(ab)(bc)/g, '$2$1') // $1表示正则表达式的第一组匹配(每对括号是一个分组),指的是`ab`;$2表示正则表达式的第二组匹配,指的是`bc`;结果是'bcab'
//$$表示$
'abbc'.replaceAll('b', '$$') // 'a$$c'
数值
//数值分隔符
//不能放在数值的最前面或最后面。
//不能两个或两个以上的分隔符连在一起。
let num = 1_000_000_000;
//没有指定间隔的位数。
123_000 === 12_300 //true
//小数和科学计数法也可以使用。
//小数点的前后不能有分隔符。
//科学计数法里面,表示指数的e或E前后不能有分隔符。
0.000_001
1e10_000
//Number()、parseInt()、parseFloat()不支持数值分隔符。
WeakRef
用于创建对象的弱引用(弱引用实例不会妨碍原始对象被GC清除)。
let target = {};
let wr = new WeakRef(target);
//deref():如果原始对象存在,返回原始对象;如果原始对象不存在,则返回undefined。
let obj = wr.deref();
if(obj){
...
}