ES6一,class相关按照构造函数形式创建类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解。在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类,但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已; 定义一个类

class Person1 {}
let person2 = class {};类的构造函数如果我们希望在创建对象的时候给类传递一些参数,这个时候应该如何做呢?每个类都可以有一个自己的构造函数(方法),这个方法的名称是固定的constructor。当我们通过new操作符,操作一个类的时候会调用这个类的构造函数constructor。每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常。

类的访问器方法在对象的属性中可以有getter和setter函数,同样在类中也有。

class Person {
  constructor(name) {
    this.name = name;
  }
  static createPerson(name) {
    return new Person(name);
  }
}
let p = Person.createPerson("sun");
console.log(p); //Person { name: "sun" }类的继承在ES6中新增了使用extends关键字,可以方便的帮助我们实现继承
class Person {}
class Student extends Person {}super关键字super关键字可以对父类中的方法进行调用。
class sArray extends Array {
  lastItem() {
    return this[this.length - 1];
  }
}
let arr = new sArray(2, 3, 4, 5, 6);
arr.push(7);
console.log(arr.lastItem());JavaScript中的多态多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口,或使用一个单一的符号来表示多个不同的类型。如此看来JavaScript是一定存在多态的。
function sum(a, b) {
  console.log(a + b);
}
sum(10, 20);
sum("abc", "cba");二,基础语法1,字面量增强写法属性增强写法是对象中代表键值的变量名称与键名一致,则可以直接缩写为一个。函数增强写法是对象中的函数,function() 可以改为 ()计算属性写法
let name = "sun";
let age = 19;
let person = {
  name,
  age,
  [name + 123]: "hello",
  eating() {
    console.log(this.name);
  },
};
console.log(person); //{ name: "sun", age: 19, sun123: "hello", eating: [Function: eating] }2,解构ES6中新增了一个从数组或对象中方便获取数据的方法,称之为解构Destructuring。数组结构:基本解构过程、顺序解构、解构出数组、默认值

let names = ["sun0", "sun1", "sun2"]; let [item1, item2, item3] = names; let [item4, item5] = names; let [, , item6] = names; let [item7, ...item8] = names; console.log(item1, item2, item3); //sun0 sun1 sun2 console.log(item4, item5); //sun0 sun1 console.log(item6); //sun2 console.log(item7, item8); //sun0 [ "sun1", "sun2" ]对象结构:基本解构过程、任意顺序、重命名、默认值

let obj = {
  name: "sun",
  age: 19,
  height: 190,
};
let { age, name: newName, height, address = "上海" } = obj;
console.log(age, newName, address); //19 sun 上海解构的应用场景解构目前在开发中使用是非常多的:
  • 比如在开发中拿到一个变量时,自动对其进行解构使用
  • 比如对函数的参数进行解构

2,let和const

var和let/const区别

  • 1,let和const不允许重复声明
  • 2,var、let、const都用于声明一个变量,const声明的变量值不能再改变,如果声明一个对象,可以修改其属性和属性值,因为存对象时保存的是地址,const保证地址不变。
  • 3,作用域提升:var有,let和const没有,但是let和const声明的变量仍旧在解析阶段被创建出来但是没有赋值也不能访问。
  • 4,window和var、let和const关系。var声明的变量会添加到window对象上,而let和const声明的变量被保存在Variable_:variableMap中
  • 5,作用域,es5中只有全局作用域和函数作用域,es6中增添了块级作用域,如if语句,switch语句,for语句,class,{}。var没有块级作用域,let和const有。
  • 6,暂时性死区,在一个代码块中,不能在声明之前去使用变量
// 暂时性死区
let obj = "hello";
function say() {
  console.log(obj);
  let obj = "nihao";
}
say();var、let、const的选择尽量不使用var,优先选择const,次选择let

3,字符串模板的使用

es6之前如果需要将字符串和变量拼接在一起需要使用到连接符 + ,但是es6之后可以使用模板字符 ``,在模板字符串中可以使用 ${expression} 写变量甚至是表达式来嵌入动态内容。

模板字符串还有另外一种用法:标签模板字符串(Tagged Template Literals),如下例。如果我们使用标签模板字符串,并且在调用的时候插入其他的变量:

  • 模板字符串被拆分了;
  • 第一个元素是数组,是被模块字符串拆分的字符串组合;
  • 后面的元素是一个个模块字符串传入的内容;
let name = "sun",
  age = 20;
console.log(`${name} is ${age} years old now!`); //sun is 20 years old now!

function foo(...args) {
  console.log(args);
}
foo("helloworld"); //[ "helloworld" ]
foo`hello${name}world${age}`; //[ [ "hello", "world", "" ], "sun", 20 ]4,展开语法展开语法 (Spread syntax), 可以在函数调用/数组构造时,将数组表达式或者 string 在语法层面展开;还可以在构造字面量对象时,将对象表达式按 key-value 的方式展开。在函数调用时使用展开语法
function sum(x, y, z) {
  return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers)); //6在 new 表达式中应用

let dateFields = [1970, 0, 1]; // 1970 年 1 月 1 日 let d = new Date(...dateFields);数组拷贝 (copy)注意,浅拷贝,只遍历一层。

let arr = [1, 2, [3]]; let arr2 = [...arr];

arr2[2].push(4);

console.log(arr2); console.log(arr); //arr也被改变了 // [ 1, 2, [ 3, 4 ] ] // [ 1, 2, [ 3, 4 ] ]连接多个数组

let arr1 = [0, 1, 2]; let arr2 = [3, 4, 5]; let arr3 = [...arr1, ...arr2]; console.log(arr3); //[ 0, 1, 2, 3, 4, 5 ]构造字面量对象时使用展开语法

var obj1 = { foo: "bar", x: 42 };
var obj2 = { foo: "baz", y: 13 };

var clonedObj = { ...obj1 };
// 克隆后的对象:{ foo: "bar", x: 42 }

var mergedObj = { ...obj1, ...obj2 };
// 合并后的对象:{ foo: "baz", x: 42, y: 13 }5,数值的表示在ES6中规范了二进制和八进制的写法:

const num1 = 100; const num2 = 0b100; //binary二进制 const num3 = 0o100; //octonary八进制 const num4 = 0x100; // hexadecimal十六进制 console.log(num1, num2, num3, num4); //100 4 64 256ES2021新增特性:数字过长时,可以使用_作为连接符

const num5 = 100_000_000; console.log(num5); //1000000006,symbolSymbol是什么呢?Symbol是ES6中新增的一个基本数据类型,翻译为符号。 那么为什么需要Symbol呢? 在ES6之前,对象的属性名都是字符串形式,那么很容易造成属性名的冲突;比如原来有一个对象,我们希望在其中添加一个新的属性和值,但是我们在不确定它原来内部有什么内容的情况下,很容易造成冲突,从而覆盖掉它内部的某个属性; Symbol就是为了解决上面的问题,用来生成一个独一无二的值。Symbol值是通过Symbol函数来生成的,生成后可以作为属性名;也就是在ES6中,对象的属性名可以使用字符串,也可以使用Symbol值;Symbol即使多次创建值,它们也是不同的:Symbol函数执行后每次创建出来的值都是独一无二的;我们也可以在创建Symbol值的时候传入一个描述description:这个是ES2019(ES10)新增的特性;

基本使用

const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); //false

const obj = {
  [s1]: "sun",
  [s2]: 20,
};
console.log(obj[s1], obj[s2]); //sun 20,只能通过obj[]访问,不能通过obj.s1获取属性值description获取描述

const s3 = Symbol("hello"); console.log(s3.description); //hello新增属性

console.log(Object.keys(obj)); //[]
console.log(Object.getOwnPropertyNames(obj)); //[];
console.log(Object.getOwnPropertySymbols(obj)); //[ Symbol(), Symbol(), Symbol(), Symbol() ]
const skeys = Object.getOwnPropertySymbols(obj);
for (let i of skeys) {
  console.log(obj[i]); //sun 20 180 河南
}相同值的Symbol前面我们讲Symbol的目的是为了创建一个独一无二的值,那么如果我们现在就是想创建相同的Symbol应该怎么来做呢?

const s1 = Symbol.for("hello"); const s2 = Symbol.for("hello"); console.log(s1 === s2); //true console.log(typeof s1); //symbol console.log(Symbol.keyFor(s1)); //hello三,函数相关1,函数的默认参数在es6中定义函数可以给参数一个默认值,当调用函数时传入参数则使用传入的参数,若没有传入参数则使用默认参数。

function add(x = 10, y = 20) {
  return x + y;
}
console.log(add(1, 2)); // 3
console.log(add()); //30配合解构使用
function foo({ name, age } = { name: "sun", age: 20 }) {
  return `${name} is ${age} years old now!`;
}
let obj1 = {
  name: "xiaoming",
  age: 18,
};
console.log(foo(obj1)); //xiaoming is 18 years old now!
console.log(foo()); //sun is 20 years old now!2,函数的剩余参数ES6中引用了rest parameter,可以将不定数量的参数放入到一个数组中。如果最后一个参数是 … 为前缀的,那么它会将剩余的参数放到该参数中,并且作为一个数组;
function foo(m, n, ...args) {
  console.log(m, n); // 1 2
  console.log(args); //[3, 4, 5, 6, 7, 8, 9, 0];
}
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
foo(...arr);剩余参数和arguments区别
  • 1,剩余参数只包含那些没有对应形参的实参,而arguments对象包含了传给函数的所有实参;
  • 2,arguments对象不是一个真正的数组,而rest参数是一个真正的数组,可以进行数组的所有操作;
  • 3,arguments是早期的ECMAScript中为了方便去获取所有的参数提供的一个数据结构,而rest参数是ES6中提供,并且希望以此来替代arguments的;
  • 4,剩余参数必须放到最后一个位置,否则会报错。

3,箭头函数

基本语法

// 箭头函数
let fun = (name) => {
    return `Hello ${name} !`;
};

// 普通函数
let fun = function (name) {
    return `Hello ${name} !`;
};箭头函数的参数
  • 如果箭头函数没有参数,直接写一个空括号即可。
  • 如果箭头函数的参数只有一个,也可以省去包裹参数的括号。
  • 如果箭头函数有多个参数,将参数依次用逗号(,)分隔,包裹在括号中即可。
  • 如果箭头函数的函数体只有一句代码,就是简单返回某个变量或者返回一个简单的JS表达式,可以省去函数体的大括号{ }。
let fun = val => val;
// 等同于
let fun = function (val) { return val };

let sum = (num1, num2) => num1 + num2;
// 等同于
let sum = function(num1, num2) {
  return num1 + num2;
};如果箭头函数的函数体只有一句代码,就是返回一个对象,可以像下面这样写:
// 用小括号包裹要返回的对象,不报错
let getTempItem = id => ({ id: id, name: "Temp" });

// 但绝不能这样写,会报错,因为对象的大括号会被解释为函数体的大括号
let getTempItem = id => { id: id, name: "Temp" };如果箭头函数的函数体只有一条语句并且不需要返回值(最常见是调用一个函数),可以给这条语句前面加一个void关键字

let fun = () => void doesNotReturn();箭头函数与普通函数的区别

  • 1,语法更加简洁、清晰
  • 2,箭头函数没有 prototype (原型),所以箭头函数本身没有this
  • 3,箭头函数不会创建自己的this。箭头函数没有自己的this,箭头函数的this指向在定义(注意:是定义时,不是调用时)的时候继承自外层第一个普通函数的this。所以,箭头函数中 this 的指向在它被定义的时候就已经确定了,之后永远不会改变。
  • 4,call | apply | bind 无法改变箭头函数中this的指向
  • 5,箭头函数不能作为构造函数使用
  • 6,箭头函数不绑定arguments,取而代之用rest参数…代替arguments对象,来访问箭头函数的参数列表
  • 7,箭头函数不能用作Generator函数,不能使用yield关键字

四,set和map

set和map

ES71,Array Includes在ES7之前,如果我们想判断一个数组中是否包含某个元素,需要通过 indexOf 获取结果,并且判断是否为 -1。在ES7中,我们可以通过includes来判断一个数组中是否包含一个指定的元素,根据情况,如果包含则返回 true,否则返回false

const x = 3; console.log(Math.pow(x, 3)); //27 console.log(x ** 3); //27ES8

1, Object.values

之前我们可以通过 Object.keys 获取一个对象所有的key,在ES8中提供了 Object.values 来获取所有的value值

const obj = {
  name: "sun",
  age: 20,
};
console.log(Object.keys(obj)); //[ "name", "age" ]
console.log(Object.values(obj)); //[ "sun", 20 ]2,Object entries通过Object.entries 可以获取到一个数组,数组中会存放可枚举属性的键值对数组
const obj = {
  name: "sun",
  age: 20,
};
console.log(Object.entries(obj)); //[ [ "name", "sun" ], [ "age", 20 ] ]3,String Padding某些字符串我们需要对其进行前后的填充,来实现某种格式化效果,ES8中增加了 padStart 和 padEnd 方法,分别是对字符串的首尾进行填充的。

const message = "Hello World"; console.log(message.padStart(15, "a")); //aaaaHello·World console.log(message.padEnd(15, "b")); //Hello worldbbbb

//场景:脱敏显示号码 const num = "17634989523"; const lastNum = num.slice(-4); const newNum = lastNum.padStart(num.length, "*"); console.log(newNum); //*******95234,Trailing Commas在ES8中,我们允许在函数定义和调用时多加一个逗号:

function foo(a, b) {
  console.log(a, b);
}
foo(10, 20);4,getOwnPropertyDescriptors获取属性描述符5,async和awaitES9

1,Async iterators

2,Object spread operators展开运算符

3,Promise finally

ES10

1,flat flatMap

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。 flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。

  • 注意一:flatMap是先进行map操作,再做flat的操作;
  • 注意二:flatMap中的flat相当于深度为1;
const obj = {
  name: "sun",
  age: 20,
};
const entriesObj = Object.entries(obj);
console.log(entriesObj); //[ [ "name", "sun" ], [ "age", 20 ] ]

const fromEntriesObj = Object.fromEntries(entriesObj);
console.log(fromEntriesObj); //{ name: "sun", age: 20 }实例:url参数转对象
const queryString = "name=sun&age=20&height=190";
const searchParams = new URLSearchParams(queryString);
console.log(searchParams); //URLSearchParams { "name" => "sun", "age" => "20", "height" => "190" }
for (let i of searchParams) {
  console.log(i); // ["name", "sun"]["age", "20"]["height", "190"];
}
const queryObj = Object.fromEntries(searchParams);
console.log(queryObj); //{ name: "sun", age: "20", height: "190" }3,trimStart、trimEnd去除一个字符串首尾的空格,我们可以通过trim方法,如果单独去除前面或者后面呢?ES10中给我们提供了trimStart和trimEnd

const str = " hello world "; console.log(str.trim() + "nihao"); //hello worldnihao console.log(str.trimStart() + "nihao"); //hello world nihao console.log(str.trimEnd() + "nihao");// hello worldnihao4,Symbol description5,Optional catch bindingES11

1,BigInt

在早期的JavaScript中,我们不能正确的表示过大的数字。 大于MAXSAFEINTEGER的数值,表示的可能是不正确的。 那么ES11中,引入了新的数据类型BigInt,用于表示大的整数:BitInt的表示方法是在数值的后面加上n

  • 比如在浏览器中可以通过this、window来获取;
  • 比如在Node中我们需要通过global来获取; 那么在ES11中对获取全局对象进行了统一的规范:globalThis

console.log(globalThis);5,for..in标准化在ES11之前,虽然很多浏览器支持for…in来遍历对象类型,但是并没有被ECMA标准化。在ES11中,对其进行了标准化,for…in是用于遍历对象的key的

const obj = {
  name: "sun",
  age: 18,
  height: 1.88,
};
for (const key in obj) {
  console.log(key); //sun age height
}6,Dynamic Import:动态导入7,Promise.allSettled8,import metaES12

1,FinalizationRegistry

FinalizationRegistry 对象可以让你在对象被垃圾回收时请求一个回调。 FinalizationRegistry 提供了这样的一种方法:当一个在注册表中注册的对象被回收时,请求在某个时间点上调用一个清理回调。(清理回调有时被称为 finalizer ); 你可以通过调用register方法,注册任何你想要清理回调的对象,传入该对象和所含的值;

2,WeakRefs

如果我们默认将一个对象赋值给另外一个引用,那么这个引用是一个强引用,如果我们希望是一个弱引用的话,可以使用WeakRef