语法小提
- 如果es6的import命令要取代Node的require方法,这就形成了一个障碍。因为require是运行时加载模块,import命令无法取代require的动态加载功能。
- ES6的模块自动采用严格模式,不管你有没有在头部加上'use strict';
- import()函数,完成动态加载
- import()类似于Node的require方法,区别主要是前者是异步加载,返回Promise对象,后者是同步加载。
import {start,exists,readFile} from 'fs';
- import()类似于Node的require方法,区别主要是前者是异步加载,返回Promise对象,后者是同步加载。
- rest参数
- ES6引入rest参数(形式为...变量名),用于获取函数的多余参数。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
注意:rest参数之后不能再有其他参数(即只能是最后一个参数)
- Set和Map数据结构
Generator函数的语法
- 简介
- Generator 函数是ES6提供的一种异步编程解决方案。语法上可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。
- 执行Generator函数会返回一个遍历器对象,也就是说Generator函数还是一个遍历器对象生成函数。
Generator函数有两个特征:
- function 关键字与函数名之间有一个星号
- 函数体内部使用yield表达式,定义不同的内部状态。
Generator函数调用与普通函数调用的区别
- 调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象。
- 必须调用遍历器对象的next方法,使得指针移向下一个状态。Generator函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
- yield表达式
- Generator函数可以不用yield表达式,这时就变成了一个单纯的暂缓执行函数。
function* f() {
console.log('执行了!')
}
var generator = f();
setTimeout(function () {
generator.next()
}, 2000);
//如果f是普通函数,在为变量generator赋值时就会执行。
Set
- [...new Set(array)]可以将 Set 结构转为数组, 也可实现数组去重
- Array.from方法可以将 Set 结构转为数组,也可实现数组去重。
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items); - Set实例的属性和方法
add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值 - 遍历操作
for...of
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
for (let item of set.values()) {
console.log(item);
}
for (let item of set.entries()) {
console.log(item);
}
//Set 结构的实例默认可遍历,直接用for...of循环遍历 Set
for (let x of set) {
console.log(x);
}
forEach()
Map
- Map 的键实际上是跟内存地址绑定的
如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键 - Map实例的属性和方法
size属性返回Map结构的成员总数 。
set(key, value):添加某个值,返回 Map 结构本身。可以采用链式写法。
get(key):读取key对应的健值,如果找不到key,返回undefined。
delete(key):删除某个健,返回一个布尔值,表示删除是否成功。
has(key):返回一个布尔值,表示该值是在当前Map对象之中。
clear():清除所有成员,没有返回值 - 遍历
for...of
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
for (let value of map.values()) {
console.log(value);
}
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
for (let [key, value] of map.entries()) {
console.log(key, value);
}
Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...)
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
结合数组的map方法、filter方法,可以实现 Map 的遍历和过滤(Map 本身没有map和filter方法)。
const map0 = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
const map1 = new Map(
[...map0].filter(([k, v]) => k < 3)
);
// 产生 Map 结构 {1 => 'a', 2 => 'b'}
const map2 = new Map(
[...map0].map(([k, v]) => [k * 2, '_' + v])
);
// 产生 Map 结构 {2 => '_a', 4 => '_b', 6 => '_c'}
forEach()
Promise对象
- Promise 对象是一个构造函数,用来生成Promise实例
function loadImgAsync(url){
return new Promise(function(resolve,reject){
const image = new Image();
image.onload = function(){
resolve(image)
}
image.onerror = function(){
reject(new Error('Could not load image at' + url))
}
image.src = url;
})
}
- Promise.all()方法用于将多个Promise实例包装成一个新的Promise实例。
- Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数
Class(类)
class Point{
constructor(){}
}
Point.prototype.constructor === Point // true prototype对象的constructor的属性,直接指向”类“的本身。
注意点
- 严格模式
类和模块的内部,默认就是模式。 - 不存在提升
类不存在变量提升。let也不存在变量提升。 - this的指向
类的方法内部如果含有this,它默认指向类的实例。但是必须非常小心,一旦单独使用该方法,很可能会报错。
class Logger {
constructor() {
this.printName = this.printName.bind(this)
}
printName() {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
const {printName } = logger;
printName(); // Uncaught TypeError: Cannot read property 'print' of undefined
上面代码中,printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境(由于 class 内部是严格模式,所以 this 实际指向的是undefined),从而导致找不到print方法而报错。
一个比较简单的解决方法是,在,这样就不会找不到print方法了
class Logger {
constructor() {
this.printName = this.printName.bind(this);
}
// ...
}
另一种解决方法是
静态方法
- 方法前加关键字
- ,如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
- 直接通过来调用(Foo.classMethod()),如果静态方法包含this关键字,这个this指的是类,而不是实例。
- 父类的静态方法可以。静态方法也是可以从对象上调用的。
实例属性的新写法
- 除了定义在constructor()方法里面的this上面,也可以定义在。
这种新写法的是,所有实例对象自身的属性都定义在类的头部,一眼就能看出这个类有哪些实例属性。
静态属性
- Class,即Class.propName,而不是定义在实例对象()上的属性
// 老写法
class Foo {
// ...
}
Foo.prop = 1; // 目前只有这种方法可行
// 新写法
class Foo{
static prop = 1;
} // 目前的提案提供前面加static关键字
私有方法和私有属性
- 只能类的访问的方法和属性,外部不能访问
- 但ES6,只能通过变通方法模拟实现
一种方法是在命名上加以区别,方法或属性前面加_
一种方法将私有方法移出模块
一种方法利用symbol值的唯一性,将私有方法的名字命名为一个symbol值 - 私有属性(方法)的,在属性名(方法)之前,使用表示
前面加上关键字,表示静态的私有属性(方法)
Symbol
- 一种原始的数据类型。Symbol值通过Symbol函数生成。
let s = Symbol(); - Symbol函数加参数区分symbol值
字符串作为参数
let s1 = Symbol('s1');
let s2 = Symbol('s2');
对象作为参数
如果Symbol函数的参数是一个对象,就会调用该对象的toString方法,将其转化为一个字符串,然后才生成一个Symbol值。所以,Symbol函数的参数只能是字符串。
Symbol值作为属性名的遍历
- 使用Object.getOwnPropertySymbols()方法获取一个对象上的Symbol属性名
let s5 = Symbol('s5');
let s6 = Symbol('s6');
let a = {
[s5]: 's5',
[s6]: 's6'
}
Object.getOwnPropertySymbols(a);// [Symbol(s5), Symbol(s6)]
- 也可以使用Reflect.ownKeys()返回所有类型的属性名,包括常规属性名和 Symbol属性名。
a.hello = 'hello';
Reflect.ownKeys(a); // ["hello", Symbol(s5), Symbol(s6)]
Symbol.for()和Symbol.keyFor()
ES6加载规则
<script type="module" src='./foo.js'></script>
//浏览器对于带有type="module"的<script>,都是异步加载,即先渲染完整个页面,执行脚本。如果页面有多个<script type="module">,它们会按照在页面出现的顺序依次执行。
<script type="module">
import utils from "./utils.js";
</script>
//内嵌网页的写法
使用babel将ES6转码为ES5
工具配置法 (配合gulp)
- 安装gulp-babel模块
cnpm install --save-dev gulp-babel
- 配置.babelrc文件 (必须配置,否则不能将es6编译成es5)
- 编写gulpfile.js文件
var gulp = require("gulp");
var babel = require("gulp-babel");
gulp.task("default", function () {
return gulp.src("src/a.js")
.pipe(babel())
.pipe(gulp.dest("lib"));
});