ECMASript 6

ECMASript 6


在 React 和 Vue 中使用了大量的 ES6 语法,本文简单的总结了 ES6 的一些基本特性


Babel转码器

Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行。可通过以下命令安装在项目目录中。

1
npm install --save-dev @babel/core

letconst命令

let 命令

let 用来声明变量,用法类似于 var,但是声明的变量只在let命令所`在的代码块有效

const 命令

const 声明常量,一旦声明不得修改赋值,必须初始化

let const var的区别

1、var 有变量提升,let 与 const 没有

2、var 定义的变量可以修改,如果不初始化会输出 undefined,不会报错。

3、const 定义的变量不可以修改,而且必须初始化。

4、let 是块级作用域,函数内部使用 let 定义后,对函数外部无影响。


解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。

1
2
3
4
5
var obj = { name: "老王", age: 20 };
// 解构为
var { name, age } = obj;
name; // 老王
age; // 20

扩展运算符

扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

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

可用于复制数组

数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。

1
2
3
4
5
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;

模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

1
2
3
4
5
6
7
8
9
$("#result").append(
"There are <b>" +
basket.count +
"</b> " +
"items in your basket, " +
"<em>" +
basket.onSale +
"</em> are on sale!"
);

上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。

1
2
3
4
5
$("#result").append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);

模板字符串嵌入变量,需要将变量写入${}

1
2
3
let name = "Bob",
time = "today";
`Hello ${name}, how are you ${time}?`;

箭头函数

ES6 允许使用“箭头”(=>)定义函数。

1
2
3
4
5
6
7
8
9
10
11
var f = () => 5;
// 等同于
var f = function() {
return 5;
};

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};

Symbol

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。


Promise 对象

promis 简介

Promise 对象用于异步操作,它表示一个尚未完成且预计在未来完成的异步操作。

  • Promise 对象有两个特点

    (1)对象的状态不受外界影响。promise 对象代表一个异步操作,分为三个状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)

    (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。改变状态的两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected。

  • Promise 对象的缺点

    (1)无法取消,一旦新建会立刻执行

    (2)如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部

    (3)当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

Promsie 用法

Promise 对象是一个构造函数,用来生成 Promise 实例

1
2
3
4
5
6
7
8
9
const promise = new Promise(function(resolve, reject) {
// ... some code

if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});

Promise 对象接收两个参数

resolve() 在异步操作成功时调用,并将异步操作的结果,作为参数传递出去

reject() 在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。


Generator 函数

Generator 函数是 ES6 提供的一种异步编程解决方案。

特征:
function 与函数名之间有个星号(*)

函数体内部使用yield表达式

1
2
3
4
5
function* helloWorldGenerator() {
yield "hello";
yield "world";
return "ending";
}

async 函数

async 函数就是 Generator 函数的语法糖,把 Generator 函数里面的星号(*)替换成 async,yield 替换成 await。

  • await 函数返回一个 Promise 对象
1
2
3
4
5
6
7
8
9
10
11
12
async function timeout(ms) {
await new Promise(resolve => {
setTimeout(resolve, ms);
});
}

async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}

asyncPrint("hello world", 50);

上面代码指定 50 毫秒以后,输出 hello world。

使用注意点

  • await 返回一个 Promise 对象,所以 await 命令最好放在 try…catch 语句中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const sleep = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(123);
resolve("abc");

// reject(new Error("我错了"));
}, 1000);
});
};

const main = async () => {
try {
let str = await sleep();
console.log(str);
console.log(456);
} catch (error) {
console.log(error.message);
}
};
main();
  • await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。

async 函数与 Promise、Generator 函数的比较,async 函数更简洁,更具有语义,但是 async 不要滥用,会造成页面堵塞


class 继承

Class 可以通过 extends 关键字实现继承

1
2
3
class Point {}

class ColorPoint extends Point {}

上面代码定义了一个 ColorPoint 类,该类通过 extends 关键字,继承了 Point 类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个 Point 类。下面,我们在 ColorPoint 内部加上代码。

1
2
3
4
5
6
7
8
9
10
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}

toString() {
return this.color + " " + super.toString(); // 调用父类的toString()
}
}

上面代码中,constructor 方法和 toString 方法之中,都出现了 super 关键字,它在这里表示父类的构造函数,用来新建父类的 this 对象。

子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用 super 方法,子类就得不到 this 对象。

1
2
3
4
5
6
7
8
class ColorPoint extends Point {}

// 等同于
class ColorPoint extends Point {
constructor(...args) {
super(...args);
}
}

另一个需要注意的地方是,在子类的构造函数中,只有调用 super 之后,才可以使用 this 关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有 super 方法才能调用父类实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}

class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color; // ReferenceError
super(x, y);
this.color = color; // 正确
}
}

上面代码中,子类的 constructor 方法没有调用 super 之前,就使用 this 关键字,结果报错,而放在 super 方法之后就是正确的。

1
2
3
4
5
6
7
8
9
class A {
static hello() {
console.log("hello world");
}
}

class B extends A {}

B.hello(); // hello world

上面代码中,hello()是 A 类的静态方法,B 继承 A,也继承了 A 的静态方法。

Object.getPrototypeOf()

Object.getPrototypeOf 方法可以用来从子类上获取父类。

1
2
Object.getPrototypeOf(ColorPoint) === Point;
// true

因此,可以使用这个方法判断,一个类是否继承了另一个类。

super 关键字

super 既可以当函数使用,也可以当对象使用

  • 当做函数调用时,代表父类的构造函数,只能用在子类的构造函数之中,用在其他地方就会报错。
1
2
3
4
5
6
7
class A {}

class B extends A {
constructor() {
super();
}
}

上面代码,子类 B 的构造函数中的super(),代表调用父类的构造函数

注意,super 虽然代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super 内部的 this 指的是 B 的实例,因此 super()在这里相当于 A.prototype.constructor.call(this)

  • super 作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A {
p() {
return 2;
}
}

class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}

let b = new B();

上面代码中,子类 B 当中的 super.p(),就是将 super 当作一个对象使用。这时,super 在普通方法之中,指向A.prototype,所以 super.p()就相当于 A.prototype.p()

这里需要注意,由于 super 指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过 super 调用的。

类的 prototype 属性和__proto__属性

(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

(2)子类 prototype 属性的__proto__属性,表示方法的继承,总是指向父类的

1
2
3
4
5
6
class A {}

class B extends A {}

B.__proto__ === A; // true
B.prototype.__proto__ === A.prototype; // true

上面代码中,子类 B 的__proto__属性指向父类 A,子类 B 的 prototype 属性的__proto__属性指向父类 A 的 prototype 属性。

子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型。

1
2
3
4
5
var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, "red");

p2.__proto__ === p1.__proto__; // false
p2.__proto__.__proto__ === p1.__proto__; // true

上面代码中,ColorPoint 继承了 Point,导致前者原型的原型是后者的原型。

因此,通过子类实例的__proto__.__proto__属性,可以修改父类实例的行为。


MiXin 模式的实现

Mixin 指的是多个对象合成一个新的对象,新对象具有各个组成成员的接口。它的最简单实现如下。

1
2
3
4
5
6
7
const a = {
a: "a"
};
const b = {
b: "b"
};
const c = { ...a, ...b }; // {a: 'a', b: 'b'}

上面代码中,c 对象是 a 对象和 b 对象的合成,具有两者的接口。


本文参考: 阮大神

-------------本文结束感谢您的阅读-------------
0%