必威体育Betway必威体育官网
当前位置:首页 > IT技术

《ES6》(阮一峰)学习笔记

时间:2019-06-15 03:41:03来源:IT技术作者:seo实验室小编阅读:61次「手机版」
 

es6 阮一峰

一、简介

  1. ECMAScript 和 javaScript 的关系是,前者是后者的规格,后者是前者的一种实现
  2. 各大浏览器的最新版本,对 ES6 的支持可以查看kangax.github.io/compat-table/es6/。随着时间的推移,支持度已经越来越高了,超过 90%的 ES6 语法特性都实现了。
  3. Node 是 JavaScript服务器运行环境(runtime)。
  4. babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行。这意味着,你可以用 ES6 的方式编写程序,又不用担心现有环境是否支持。

    下面的命令在项目目录中,安装 Babel:

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

  5. Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API,可以必须使用babel-polyfill,为当前环境提供一个垫片。

二、变量声明

ES5 只有两种声明变量的方法:var命令和function命令。

ES6 一共有 6 种声明变量的方法:var命令、function命令、let、const命令、import命令和class命令。

2.1 let命令

(1)基本用法–块级作用域

  1. 原本JS只有函数作用域,没有块级作用域。ES6 新增了let变量声明命令,声明的变量仅在块级作用域内有效
  2. for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
  3. for (let i = 0; i < 3; i++) { let i = 'abc'; console.log(i); } // abc // abc // abc

(2)不存在变量提升

之前由于JS的预处理机制,var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。

let命令所声明的变量一定要在声明后使用,否则报错,因为预处理机制只找var和function

(3)会有暂时性死区、不允许重复声明

(4)块级作用域与函数声明

考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句

2.2 const命令

(1)基本用法

const声明一个只读的常量。一旦声明,常量的值就不能改变,且只在声明所在的块级作用域内有效。

这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动,对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址)

(2)其他同let

不存在变量提升、会有暂时性死区、不允许重复声明

三、变量的解构赋值

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

解构赋值的拷贝是浅拷贝

结构规则:

  1. 解构成对象,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。
  2. 解构成数组,等号右边必须为可迭代对象

3.1 用途

(1)交换变量的值

let x = 1;
let y = 2;

[x, y] = [y, x];

(2)从函数返回多个值

// 返回一个数组

function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();

// 返回一个对象

function example() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = example();

(3)函数参数的定义

(4)提取 JSON 数据

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let { id, status, data: number } = jsonData;

console.log(id, status, number);
// 42, "OK", [867, 5309]

(5)函数参数的默认值

(6)遍历 Map 结构

(7)输入模块的指定方法

3.2 参考文献

https://blog.csdn.net/qq_17175013/article/details/81490923

四、字符串的扩展

4.1 方法扩展

  1. repeat方法返回一个新字符串,表示将原字符串重复n次。
  2. indexof方法外,定义了三种方法

    includes():返回布尔值,表示是否找到了参数字符串。

    startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。

    endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

4.2 模板字符串

用用反引号(`)标识,可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

模板字符串中嵌入变量,需要将变量名写在${}之中。

五、数值的扩展

5.1 方法扩展

  1. 在Number对象上,新提供了Number.isFinite()和Number.isNaN()两个方法
  2. Number.isinteger() 用来判断一个数值是否为整数。

六、函数的扩展

6.1 rest 参数(形式为…变量名)

rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

arguments对象不是数组,而是一个类似数组的对象。rest 参数是一个真正的数组,数组特有的方法都可以使用。

注:

rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

// 报错 function f(a, ...b, c) { // ... }

函数的length属性,不包括 rest 参数。

(function(a) {}).length // 1 (function(...a) {}).length // 0 (function(a, ...b) {}).length // 1

6.2 name属性

返回该函数的函数名。

function foo() {}
foo.name // "foo"

6.3 箭头函数

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

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

注:

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

6.4 函数绑定运算符

双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。

七、数组的扩展

7.1 entries(),keys()和values()——用于遍历数组

它们都返回一个遍历器对象,可以用for…of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

八、对象的扩展

8.1 super关键字

this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。

九、symbol

9.1 概述

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

是一种类似于字符串的数据类型,不能使用new命令,不能添加属性,不能与其他类型的值进行运算,因为生成的 Symbol 是一个原始类型的值,不是对象。

Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

十、Set数据结构

10.1 Set

  1. 类似于数组,但是成员的值都是唯一的,没有重复的值。所以可以用来去重
// 去除数组的重复成员
[...new Set(array)]
//去除字符串里面的重复字符
[...new Set('ababbc')].join('');// "abc"

//Array.from方法可以将 Set 结构转为数组。
//这就提供了去除数组重复成员的另一种方法。
function dedupe(array) {
  return Array.from(new Set(array));
}

dedupe([1, 1, 2, 3]) // [1, 2, 3]
  1. 不会发生类型转换,所以5和"5"是两个不同的值。
  2. Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

add(value):添加某个值,返回 Set 结构本身。

delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

has(value):返回一个布尔值,表示该值是否为Set的成员。

clear():清除所有成员,没有返回值。

  1. Set 结构的实例有四个遍历方法,可以用于遍历成员。

keys():返回键名的遍历器

values():返回键值的遍历器

entries():返回键值对的遍历器

foreach():使用回调函数遍历每个成员,没有返回值

10.2 WeakSet

  1. WeakSet 结构与 Set 类似,也是不重复的值的集合。
  2. WeakSet 的成员只能是对象,而不能是其他类型的值。WeakSet 的成员只能是对象,而不能是其他类型的值。
  3. WeakSet 中的对象都是弱引用,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

10.3 Map

JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

  1. 方法以及属性:

    (1)size 属性,返回 Map 结构的成员总数

    (2)set(key, value),设置键名key对应的键值为value

    (3)get(key),读取key对应的键值,如果找不到key,返回undefined。

    (4)has(key),表示某个键是否在当前 Map 对象之中。

    (5)delete(key),delete方法删除某个键,返回true。如果删除失败,返回false。

    (6) clear(),清除所有成员,没有返回值。

  2. 四个遍历方法,可以用于遍历成员。

keys():返回键名的遍历器。

values():返回键值的遍历器。

entries():返回所有成员的遍历器。

forEach():遍历 Map 的所有成员。

  1. 与其他数据结构的互相转换

    (1)Map 转为数组

    Map 转为数组最方便的方法,就是使用扩展运算符(…)。

    (2)数组 转为 Map

    将数组传入 Map 构造函数,就可以转为 Map。

    (3)Map 转为对象

    如果所有 Map 的键都是字符串,它可以无损地转为对象。如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名

十一、Proxy(代理器)

11.1 概述

目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写,在内部重新定义拦截函数。

十二、Promise 对象

12.1 含义

  1. Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
  2. Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
  3. Promise对象有以下两个特点:

    (1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

    (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

  4. 优点:

    (1)可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。

    (2)提供统一的接口,使得控制异步操作更加容易。

  5. 缺点:

    (1)无法取消Promise,一旦新建它就会立即执行,无法中途取消。

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

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

    所以处理异步可以选择Generator

12.2 基本用法

  1. Promise对象是一个构造函数,用来生成Promise实例。
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(ERROR);
  }
});
  1. Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});
  1. Promise.prototype.then()方法

    作用是为 Promise 实例添加状态改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

  2. Promise.prototype.catch()

    用于指定发生错误时的回调函数。

  3. Promise.prototype.finally()

    不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

  4. Promise.all()

    用于将多个 Promise 实例,包装成一个新的 Promise 实例

12.3 应用

  1. 加载图片

    我们可以将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化。

  2. Generator 函数与 Promise 的结合

    使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个Promise对象。

十三、Iterator 和 for…of 循环

13.1 Iterator(遍历器)的概念

JavaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。

遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

13.2 Iterator(遍历器)的作用及遍历过程

  1. 作用

    一是为各种数据结构,提供一个统一的、简便的访问接口;

    二是使得数据结构的成员能够按某种次序排列;

    三创造了一种新的遍历命令for…of循环

  2. 遍历过程

(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

13.3 for…of 循环

for…of循环,作为遍历所有数据结构的统一的方法,for…of循环内部调用的是数据结构的Symbol.iterator方法。

for…of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、Generator 对象,以及字符串。

for…in循环读取键名,for…of循环读取键值。如果要通过for…of循环,获取数组的索引,可以借助数组实例的entries方法和keys方法

var arr = ['a', 'b', 'c', 'd'];

for (let a in arr) {
  console.log(a); // 0 1 2 3
}

for (let a of arr) {
  console.log(a); // a b c d
}

注:

对于普通的对象,for…of结构不能直接使用,会报错,必须部署了 Iterator 接口后才能使用。但是,这样情况下,for…in循环依然可以用来遍历键名。第一种解决办法是使用Object.keys方法将对象的键名生成一个数组,然后遍历这个数组。另一个方法是使用 Generator 函数将对象重新包装一下。

十四、Generator 函数

14.1 基本概念

Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个Iterator对象。

Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();

14.2 yield 表达式

由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。

yield表达式就是暂停标志。

yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。

注:yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行。

14.3 处理异步

在前面说过Promise有一些缺点,我们可以用Generator 函数实现异步编程

协程有点像函数,又有点像线程。它的运行流程大致如下。

第一步,协程A开始执行。

第二步,协程A执行到一半,进入暂停,执行权转移到协程B。

第三步,(一段时间后)协程B交还执行权。

第四步,协程A恢复执行。

在Generator 函数中,协程遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。它的最大优点,就是代码的写法非常像同步操作。

十五、async函数

15.1 含义

Generator 函数的语法糖。

//Generator 函数
const gen = function* () {
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
//async函数
const asyncReadFile = async function () {
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。

15.2 优点

async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。

async函数对 Generator 函数的改进,体现在以下四点:

(1)内置执行器。

(2)更好的语义。

async和await,比起星号和yield,语义更清楚了。

(3)更广的适用性

co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值

(4)返回值是 Promise。

async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便,可以用then方法指定下一步的操作。

15.3 使用

await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。可以将需判断的await放再try…catch结构里面,不管这个异步操作是否成功,接下来的await都会执行。

十六、Class

16.1 定义

啦啦啦,JS也有类了

通过class关键字,可以定义类,类的内部所有定义的方法,都是不可枚举的

ES6 的类,完全可以看作构造函数的另一种写法。

class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor // true

16.2 私有方法和属性、静态属性

私有:引入一个新的前缀#表示私有属性或私有方法,代替private关键字,外部不能访问

class Foo {
  #a;
  #b;
  constructor(a, b) {
    this.#a = a;
    this.#b = b;
  }
  #sum() {
    return #a + #b;
  }
  printSum() {
    console.log(this.#sum());
  }
}

静态属性:使用static关键字static myStaticProp = 42;

16.3 new.target 属性

用来返回new命令作用于的那个构造函数,如果构造函数不是通过new命令或Reflect.construct()调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。

子类继承父类时,new.target会返回子类。利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。

16.4 继承

通过extends关键字实现继承

十七、Module(模块)

17.1 概念

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

优点:

(1)将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者navigator对象的属性。

(2)不再需要对象作为命名空间(比如Math对象),未来这些功能可以通过模块提供。

17.2 引入和使用

模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

  1. export

    一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量、函数或者类。

// 变量
var firstName = 'Michael';
var lastName = 'jackson';
var year = 1958;

export {firstName, lastName, year};
// 函数
export function multiply(x, y) {
  return x * y;
};

通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。

function v1() { ... }
function v2() { ... }
//as关键字,重命名了函数v1和v2的对外接口。重命名后,v2可以用不同的名字输出两次。
export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};
  1. 使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。import命令具有提升效果,会提升到整个模块的头部,首先执行。
import {firstName, lastName, year} from './profile.js';

function setName(element) {
  element.textcontent = firstName + ' ' + lastName;
}

//如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。
import { lastName as surname } from './profile.js';

import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。

import {a} from './xxx.js'

a = {}; // Syntax Error : 'a' is read-only;

上面代码中,脚本加载了变量a,对其重新赋值就会报错,因为a是一个只读的接口。但是,如果a是一个对象,改写a的属性是允许的。

import {a} from './xxx.js'

a.foo = 'hello'; // 合法操作

17.3 几种模块化方法的比较

十八、编码规范

  1. 静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。
// bad
const a = "foobar";
const b = 'foo' + a + 'bar';

// acceptable
const c = `foobar`;

// good
const a = 'foobar';
const b = `foo${a}bar`;
  1. 使用数组成员对变量赋值时,优先使用解构赋值。
  2. 使用扩展运算符(…)拷贝数组。
// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];

使用 Array.from 方法,将类似数组的对象转为数组。

const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
  1. 如果只是需要key: value的数据结构,使用 Map 结构。因为 Map 有内建的遍历机制。

相关阅读

es6入门到五连绝世之双杀(double kill)

es6入门到五连绝世之双杀(double kill )欢迎来到e6语法笔记教学一、解构赋值篇1.1、对象解构1.2、数组解构1.3、字面量解构二、循环

es6之数组的flat(),flatMap()

数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维数组。该方法返回一个新数组,对原数据没有影响。

require.js的用法-阮一峰

一、为什么要用require.js? 最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文

阮一峰 Webpack 教程

写在开头: 此 Webpack 教程是阮老师在 Webpack 1.x 的版本上做的。现在 Webpack 的版本已经改动较大,建议有基础的同学,就直接上官网

ES6 学习(二)[多行字符串``,重复字符串repeat,原始字符串

1\      我们平时定义字符串一般用引号(单引号,双引号),这些定义的单行字符串内部不能出现特殊字符(换行符等等),我们可以通过转义

分享到:

栏目导航

推荐阅读

热门阅读