ES6的新特性
1.const与let变量
let 与 var的区别
let 块级作用域,不能重复定义,存在死区,不会变量提升
使用var带来的麻烦
function getClothing(isCold) {
if (isCold) {
var freezing = 'Grab a jacket!';
} else {
var hot = 'It's a shorts kind of day.';
console.log(freezing);
}
}
复制代码
问:getClothing(false) 输出是什么?
答:undefined
在函数执行之前,所有的变量都会被提升至函数作用域的顶部。相当于:
var freezing ;
var hot;
function getClothing(isCold) {
if (isCold) {
freezing = 'Grab a jacket!';
} else {
hot = 'It's a shorts kind of day.';
console.log(freezing);
}
}
复制代码
而let与const声明解决了这个问题,因为他们是块级作用域,在代码块(用{}表示)
function getClothing(isCold) {
if (isCold) {
const freezing = 'Grab a jacket!';
} else {
const hot = 'It's a shorts kind of day.';
console.log(freezing);
}
}
复制代码
答: ReferenceError: freezing is not defined
关于使用规则:
- 使用let声明的变量可以重新赋值,但是不能再同一作用域内重新声明
- 使用const声明的变量必须赋值初始化,但是不能再同一作用域内重新声明也无法重新赋值
2.模板字面量
在ES6之前,将字符串连接到一起的方法是 + 或者 concat() 方法,如:
const student = {
name: 'Richard Kalehoff',
guardian: 'Mr. Kalehoff'
};
const teacher = {
name: 'Mrs. Wilson',
room: 'N231'
}
let message = student.name + ' please see ' + teacher.name + ' in ' + teacher.room + ' to pick up your report card.';
复制代码
模板字面量本质上是包含嵌入式表达式的字符串字面量.
模板字面量用倒引号( `` )
let message = `${student.name} please see ${teacher.name} in ${teacher.room} to pick up your report card.`;
复制代码
3.解构
使用解构从数组和对象提取值并赋值给独特的变量
解构数组的值:
const point = [10, 25, -34];
const [x, y, z] = point;
console.log(x, y, z);
复制代码
输出:10,25,-34
[]表示被解构的数组,X,Y,Z表示要将数组中的值存储在其中的变量。在解构数组中,还可以忽略值,如:
const[x,,z]=point
复制代码
即忽略Y坐标
解构对象的值:
const gemstone = {
type: 'quartz',
color: 'rose',
karat: 21.29
};
const {type, color, karat} = gemstone;
console.log(type, color, karat);
复制代码
{}表示被解构的对象,type、color、karat表示要将对象中的属性存储到其中的变量。
4.对象字面量简写法
let type = 'quartz';
let color = 'rose';
let carat = 21.29;
const gemstone = {
type: type,
color: color,
carat: carat
};
console.log(gemstone);
复制代码
这样赋值很麻烦。所以:
使用和所分配的变量名称相同的名称初始化对象时,可以按照下面方式赋值。
let type = 'quartz';
let color = 'rose';
let carat = 21.29;
const gemstone = {type,color,carat};
console.log(gemstone);
复制代码
还可以将函数简写:
let gemstone = {
type,
color,
carat,
calculateWorth() { ... }
};
复制代码
5.for...of循环
首先,for of 可以循环任何可迭代类型的数据。默认情况下,包括以下数据类型:String、Array、Map、Set,注意不包含Object数据类型。
for...in 循环
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {
console.log(digits[index]);
}
复制代码
注意:依然要使用index来访问数组的值
并且:当你想向数组中添加额外的方法或者另一个对象时,for...in方法会访问所有可枚举的属性,意味着如果向数组中的原型中添加任何其他属性,这些属性也会出现在循环中。
forEach 循环
注意:只能作用于数组
for...of 循环
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {
console.log(digit);
}
复制代码
注意:for...of可以忽略索引,并且可以随时退出for...of循环
for (const digit of digits) {
if (digit % 2 === 0) {
continue;
}
console.log(digit);
}
复制代码
不需要担心想对象中添加新的属性。for...of循环将值循环访问对象中的值
Array.prototype.decimalfy = function() {
for (i = 0; i < this.length; i++) {
this[i] = this[i].toFixed(2);
}
};
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const digit of digits) {
console.log(digit);
}
复制代码
输出什么?具体for..of和for...in区别,请前往。。。
6.展开运算符
展开运算符 (用三个连续的点 ... 表示),可以将字面量对象展开为多个元素
const books = ["Don Quixote", "The Hobbit", "Alice in Wonderland", "Tale of Two Cities"];
console.log(...books);
复制代码
问:输出什么?
Don Quixote The Hobbit Alice in Wonderland Tale of Two Cities
复制代码
展开运算符的一个用图是结合数组
对比concat
const fruits = ["apples", "bananas", "pears"];
const vegetables = ["corn", "potatoes", "carrots"];
const produce = fruits.concat(vegetables);
console.log(produce);
复制代码
const fruits = ["apples", "bananas", "pears"];
const vegetables = ["corn", "potatoes", "carrots"];
const produce = [...fruits,...vegetables];
console.log(produce);
复制代码
输出:["apples", "bananas", "pears", "corn", "potatoes", "carrots"]
7.剩余参数
展开运算符将数组转换为多个元素
剩余参数可以将多个元素绑定到一个数组
用法1:将变量赋数组值
const order = [20.17, 18.67, 1.50, "cheese", "eggs", "milk", "bread"];
const [total, subtotal, tax, ...items] = order;
console.log(total, subtotal, tax, items);
复制代码
react经常用法
const {namespaceId,communityId,organizationId} = this.state
let values = this.state
this.setState({
...values,
name:name,
namespaceId:11 //覆盖原值
})
复制代码
用法2:可变参数函数
对于参数不固定的函数,ES6之前用的是 参数对象(arguments) 处理
function sum() {
let total = 0;
for(const argument of arguments) {
total += argument;
}
return total;
}
复制代码
在ES6中,可以使用剩余参数运算符
function sum(...nums) {
let total = 0;
for(const num of nums) {
total += num;
}
return total;
}
复制代码
8.默认值与解构
1.默认值与解构数组
function createGrid([width = 5, height = 5] = []) {
return `Generating a grid of ${width} by ${height}`;
}
createGrid([]); // Generates a 5 x 5 grid
createGrid([2]); // Generates a 2 x 5 grid
createGrid([2, 3]); // Generates a 2 x 3 grid
createGrid([undefined, 3]); // Generates a 5 x 3 grid
复制代码
function createSundae({scoops = 1, toppings = ['Hot Fudge']} = {}) { … }
复制代码
有三个知识点:
- 解构赋值,先取参数中的值
- 默认值
- 可以是数组,对象
Javascript类
ES5创建类:
function Plane(numEngines) {
this.numEngines = numEngines;
this.enginesActive = false;
}
// 由所有实例 "继承" 的方法
Plane.prototype.startEngines = function () {
console.log('starting engines...');
this.enginesActive = true;
};
复制代码
ES6创建类:
class Plane {
//constructor方法虽然在类中,但不是原型上的方法,只是用来生成实例的.
constructor(numEngines) {
this.numEngines = numEngines;
this.enginesActive = false;
}
//原型上的方法, 由所有实例对象共享.
startEngines() {
console.log('starting engines…');
this.enginesActive = true;
}
}
console.log(typeof Plane); //function
复制代码
注意:
- constructor方法虽然在类中,但不是原型上的方法,只是用来生成实例的
- 原型上的方法, 由所有实例对象共享
Javascript中的类其实只是==function==,方法之间不能使用逗号区分属性和方法。
即:console.log(typeof Plane);
输出: function
9.Super 和 Extends
super不仅仅是一个关键字,还可以作为函数和对象
函数
在子类集成父类中,super作为函数调用,只能写在子类的构造函数(constructor)里,super代表的是父类的构造函数。
注:super() 代表的是子类,super()里面的this指向子类的实例对象this
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();//这里的super相当于A类的constructor构造函数,会执行A的constructor,但是此时的this指
//向的是B,所以打印出B
//换一种方法理解是:在执行super时,A把constructor方法给了B,此时B有了A的功能,但是执
//行的是B的内容,也就是es5的A.prototype.constructor.call(this)。
}
}
new A() // A
new B() // B
复制代码
对象
super作为对象使用时,分为在普通方法中使用和在静态方法中使用
逻辑抽象一:
ES6规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例
class A {
constructor() {
this.x = 1;
}
print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() {
super.print();
}
}
let b = new B();
b.m() // 2
复制代码
super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()内部的this指向子类B的实例
super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。
class Parent {
static myMethod(msg) {
console.log('static', msg);
}
myMethod(msg) {
console.log('instance', msg);
}
}
class Child extends Parent {
static myMethod(msg) {
super.myMethod(msg);
}
myMethod(msg) {
super.myMethod(msg);
}
}
Child.myMethod(1); // static 1
var child = new Child();
child.myMethod(2); // instance 2
复制代码
什么是super
super是一种特殊的语法,那么有两个问题
1. 既然 super 是一个可以调用的东西,它是一个函数么?
2. 对于 extends 的 class,constructor 里可以没有 super 么?
第一个问题:
输出什么?
<script>
class A extends Object {
constructor() {
const a = super;
a();
}
};
</script>
复制代码
答:
Uncaught SyntaxError: 'super' keyword unexpected here
复制代码
super 的词法定义是伴随后面那对括号的,它和 this 不同。this 的定义是 this 这个关键字会被替换成一个引用,而 super 则是 super(…) 被替换成一个调用。而且 super 除了在 constructor 里直接调用外还可以使用 super.xxx(…) 来调用父类上的某个原型方法,这同样是一种限定语法。
<script>
class A extends Array {
push(...args) {
args.forEach(item => super.push(item + 1));
}
}
let a = new A();
a.push(1, 2, 3);
console.log(a); // [2, 3, 4]
</script>
复制代码
So, super 是什么,它只是一个关键字而已。用法应该是 super(…) 或者 super.xxx(…)
第二个问题:
第二个问题实际上是问 super(…) 到底做了什么。其实 super(…) 做的事情就是生成一个 this。因为在原型继承中,如果一个类要继承自另一个类,那就得先实例化一次它的父类作为作为子类的原型。如果不做这件事,子类的原型就不能确定,当然也就无法创建 this。所以如果在 constructor 中没有 super(…) 就企图获取 this 就会报错。
<script>
class A {}
class B extends A {
constructor() {}
}
new B(); // throw
</script>
复制代码
其实 constructor 中是可以没有 super(…) 的,它只不过是用来生成 this 的而已。如果只想继承原型,而根本不想管父类的构造器,可以完全避开 this,在 constructor 中手动返回一个对象,比如:
<script>
class A {
get one() { return 1; }
}
class B extends A {
constructor() {
return Object.create(new.target.prototype);
}
get two() { return 2; }
};
let b = new B();
console.log(b.one, b.two); // 1 2
</script>
复制代码
10.箭头函数
简写主体语法
const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(
name => name.toUpperCase()
);
复制代码
当参数有多个的时候,需要使用常规主体语法
- 将函数主体放在花括号内
- 需要使用return语句来返回内容
Javascript标准函数this
1.NEW 对象
const mySundae = new Sundae('Chocolate', ['Sprinkles', 'Hot Fudge']);
复制代码
sundae这个构造函数内的this的值是实例对象, 因为他使用new被调用.
2.指定的对象
const result = obj1.printName.call(obj2);
复制代码
函数使用call/apply被调用,this的值指向指定的obj2,因为call()第一个参数明确设置this的指向
3.上下文对象
data.teleport();
复制代码
函数是对象的方法, this指向就是那个对象,此处this就是指向data.
4.全局对象或undefined
teleport();
复制代码
此处是this指向全局对象,在严格模式下,指向undefined.
箭头函数和THIS
对于普通函数, this的值基于函数如何被调用, 对于箭头函数,this的值基于函数周围的上下文, 换句话说,this的值和函数外面的this的值是一样的.
function IceCream() {
this.scoops = 0;
}
// 为 IceCream 添加 addScoop 方法
IceCream.prototype.addScoop = function() {
setTimeout(function() {
this.scoops++;
console.log('scoop added!');
console.log(this.scoops); // undefined+1=NaN
console.log(dessert.scoops); //0
}, 500);
};
----------
标题
const dessert = new IceCream();
dessert.addScoop();
复制代码
传递给 setTimeout() 的函数被调用时没用到 new、call() 或 apply(),也没用到上下文对象。意味着函数内的 this 的值是全局对象,不是 dessert 对象。实际上发生的情况是,创建了新的 scoops 变量(默认值为 undefined),然后递增(undefined + 1 结果为 NaN);
使用闭包可以解决问题:
// 构造函数
function IceCream() {
this.scoops = 0;
}
// 为 IceCream 添加 addScoop 方法
IceCream.prototype.addScoop = function() {
const cone = this; // 设置 `this` 给 `cone`变量
setTimeout(function() {
cone.scoops++; // 引用`cone`变量
console.log('scoop added!');
console.log(dessert.scoops);//1
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
复制代码
通过箭头函数也可以解决问题:
// 构造函数
function IceCream() {
this.scoops = 0;
}
// 为 IceCream 添加 addScoop 方法
IceCream.prototype.addScoop = function() {
setTimeout(() => { // 一个箭头函数被传递给setTimeout
this.scoops++;
console.log('scoop added!');
console.log(dessert.scoops);//1
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
复制代码
assign浅拷贝
可以通过Json.parse(Json.stringfy()),实现深拷贝,不过缺点是内容为null的属性将呗忽略
assign是浅拷贝
Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)
ES7新特性
Array.prototype.includes
它是一个替代indexOf,开发人员用来检查数组中是否存在值
indexOf是一种尴尬的使用,因为它返回一个元素在数组中的位置或者-1当这样的元素不能被找到的情况下。所以它返回一个数字,而不是一个布尔值。开发人员需要实施额外的检查,Array.prototype.indexOf返回-1变成了true(转换成true),但是当匹配的元素为0位置时候,该数组包含元素,却变成了false。
let arr = ['react', 'angular', 'vue']
// WRONG
if (arr.indexOf('react')) { // 0 -> evaluates to false, definitely as we expected
console.log('Can use React') // this line would never be executed
}
// Correct
if (arr.indexOf('react') !== -1) {
console.log('Can use React')
}
复制代码
在ES7中使用方法
let arr = ['react', 'angular', 'vue']
// Correct
if (arr.includes('react')) {
console.log('Can use React')
}
复制代码
还可以在字符串中使用
let str = 'React Quickly'
// Correct
if (str.toLowerCase().includes('react')) { // true
console.log('Found "react"')
}
复制代码
Exponentiation Operator(求幂运算)
ES7通过 ** 来表示求幂运算
let a = 7 ** 12
let b = 2 ** 7
console.log(a === Math.pow(7,12)) // true
console.log(b === Math.pow(2,7)) // true
复制代码
let a = 7
a **= 12
let b = 2
b **= 7
console.log(a === Math.pow(7,12)) // true
console.log(b === Math.pow(2,7)) // true
复制代码
ES8新特性
Object.values/Object.entries
Object.values和 Object.entries是在ES2017规格中,它和Object.keys类似,返回数组类型,其序号和Object.keys序号对应
Object.values,Object.entries和Object.keys各自项返回是数组,相对应包括value或者可枚举特定对象property/attribute和key
Object.values返回对象自身可以迭代属性值**(values)**为数组类型。我们最好使用Array.prototype.forEach迭代它,结合ES6的箭头函数隐形返回值:
let obj = {a: 1, b: 2, c: 3}
Object.values(obj).forEach(value=>console.log(value)) // 1, 2, 3
复制代码
or
let obj = {a: 1, b: 2, c: 3}
for (let value of Object.values(obj)) {
console.log(value)
}
// 1, 2, 3
复制代码
Object.entries,在另一方面,将会返回对象自身可迭代属性key-value对数组(作为一个数组),他们(key-value)分别以数组存放数组中。
let obj = {a: 1, b: 2, c: 3}
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key} is ${value}`)
})
// a is 1, b is 2, c is 3
复制代码
or
let obj = {a: 1, b: 2, c: 3}
for (let [key, value] of Object.entries(obj)) {
console.log(`${key} is ${value}`)
}
// a is 1, b is 2, c is 3
复制代码
字符填充函数padStart 和 padEnd
**padStart()**在开始部位填充,返回一个给出长度的字符串,填充物给定字符串,把字符串填充到期望的长度。从字符串的左边开始(至少大部分西方语言),一个经典例子是使用空格创建列
console.log('react'.padStart(10).length) // " react" is 10
console.log('backbone'.padStart(10).length) // " backbone" is 10
复制代码
padEnd顾名思义就是从字符串的尾端右边开始填充。第二个参数,你能实际上用一个任何长度的字符串。例如:
console.log('react'.padEnd(10, ':-)')) // "react:-):-" is 10
console.log('backbone'.padEnd(10, '*')) // "backbone**" is 10
复制代码
Object.getOwnProperDescriptors
返回一个对象
const obj = {
foo: 123,
get bar() { return 'abc' }
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
复制代码
上面代码中,Object.getOwnProperDescriptors 方法返回一个对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性描述对象。
后面加上key则返回对应key值的属性
Object.getOwnPropertyDescriptor(target1, 'foo')
// { value: undefined,
// writable: true,
// enumerable: true,
// configurable: true }
复制代码
用于解决Object.assign() 无法正确拷贝 get 属性和 set 属性的问题。
函数参数列表和调用中的尾逗号
现在你可以到处使用逗号,甚至最后参数都可以。
异步函数
异步函数中有两个新的关键字async和await
声明异步函数
/*使用关键字 async 声明异步函数. 参数声明和普通函数没有任何的区别*/
async function testAsync(a, b){
console.log(a, b);
return "异步";
}
/*调用异步函数, 返回值是一个 Promise 对象*/
var promise = testAsync(10, 20);
console.log(promise);
复制代码
使用关键字async声明异步函数,返回一个promise,也可以在函数中添加return 'abc',则会吧这个值封装成Promise.resolve('abc'),如果想返回一个reject的Promise,则在函数中抛个异常即可。
await
如果异步函数内没有出现await,则异步函数没什么特别的,但如果有,则:
在等待一个异步任务的完成. 一般是等待一个 Promise resolve. 当然可以等任何其他的值.
function resolve2Second(x){
return new Promise(function (resolve, reject){
setTimeout(() => resolve(x), 2000);
})
}
async function add(){
var a = await resolve2Second(20); // 2 秒钟之后 a为 20
var b = await resolve2Second(30); // 2 秒钟之后 b为30
return a + b; //
}
add().then(function (value){ // 4s中之后, 返回的promsie 会变成已决状态, 执行then中的函数
console.log(value); // 50
})
复制代码
注:await只能在异步函数中执行,同时,调用async函数虽然有等待,但并不会导致阻塞,因为他内部的所有阻塞都封装在Promise对象中异步执行
异步函数的优势
1.多个任务,每个任务依赖上个任务的结果的场景
2.如果异步代码配合await,会像同步代码可读性高。
ES9新特性
异步迭代
ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了next()方法返回一个Promise。因此await可以和for...of循环一起使用,以串行的方式运行异步操作。
async function process(array) {
for await (let i of array) {
doSomething(i);
}
}
复制代码
Promise.finally()
一个Promise调用链要么成功到达最后一个.==then()==,要么失败触发.==catch()==。在某些情况下,你想要在无论Promise运行成功还是失败,运行相同的代码,例如清除,删除对话,关闭数据库连接等。
. ==finally()== 允许你指定最终的逻辑:
function doSomething() {
doSomething1()
.then(doSomething2)
.then(doSomething3)
.catch(err => {
console.log(err);
})
.finally(() => {
// finish here!
});
}
复制代码
Rest/Spread 属性
Rest参数语法允许我们将一个布丁( 不定)数量的参数表示为一个数组。
restParam(1, 2, 3, 4, 5);
function restParam(p1, p2, ...p3) {
// p1 = 1
// p2 = 2
// p3 = [3, 4, 5]
}
复制代码
为对象解构提供了和数组一样的Rest参数()和展开操作符.
const myObject = {
a: 1,
b: 2,
c: 3
};
const { a, ...x } = myObject;
// a = 1
// x = { b: 2, c: 3 }
复制代码
正则表达式命名捕获组
ES2018允许命名捕获组使用符号 ? 在打开捕获括号(后立即命名,示例如下:
const
reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
match = reDate.exec('2018-04-30'),
year = match.groups.year, // 2018
month = match.groups.month, // 04
day = match.groups.day; // 30
复制代码
任何匹配失败的命名组都将返回undefined;
正则表达式反向断言(lookbehind)
先行断言(lookahead):这意味着匹配会发生,但不会有任何捕获,并且断言没有包含在整个匹配字段中
const
reLookahead = /\D(?=\d+)/,
match = reLookahead.exec('$123.89');
console.log( match[0] ); // $
复制代码
反向断言(lookbehind):注意尖括号
const
reLookbehind = /(?<=\D)\d+/,
match = reLookbehind.exec('$123.89');
console.log( match[0] ); // 123.89
复制代码
以上是 肯定反向断言,非数字\D必须存在。同样的,还存在 否定反向断言,表示一个值必须不存在,例如:注意感叹号
const
reLookbehindNeg = /(?<!\D)\d+/,
match = reLookbehind.exec('$123.89');
console.log( match[0] ); // null
复制代码
ES10新特性
还在观望哪些是实用的特性
个人对以下比较感兴趣
Bigint 类型
动态导入
将导入分配给变量
element.addEventListener('click', async() => {
const module = await import(`./api-scripts/button-click.js`);
module.clickEvent();
})
复制代码
Array.flat()
扁平化多维数组
let multi = [1,2,3,[4,5,6,[7,8,9,[10,11,12]]]];
multi.flat(); // [1,2,3,4,5,6,Array(4)]
multi.flat().flat(); // [1,2,3,4,5,6,7,8,9,Array(3)]
multi.flat().flat().flat(); // [1,2,3,4,5,6,7,8,9,10,11,12]
multi.flat(Infinity); // [1,2,3,4,5,6,7,8,9,10,11,12]
复制代码
Array.flatMap()
let array = [1, 2, 3, 4, 5];
array.flatMap(v => [v, v * 2]);
[1, 2, 2, 4, 3, 6, 4, 8, 5, 10]
复制代码
Object.fromEntries
可以将键值对列表转换为对象
let obj = { apple : 10, orange : 20, banana : 30 };
let entries = Object.entries(obj);
entries;
(3) [Array(2), Array(2), Array(2)]
0: (2) ["apple", 10]
1: (2) ["orange", 20]
2: (2) ["banana", 30]
let fromEntries = Object.fromEntries(entries);
{ apple: 10, orange: 20, banana: 30 }
复制代码
String.trimStart() 与 String.trimEnd()
let greeting = " Space around ";
greeting.trimEnd(); // " Space around";
greeting.trimStart(); // "Space around ";
复制代码
重写了sort和toString 方法
文章仅用于个人整理,借鉴了各路大神的整理,参考链接: