收录 项目开发当中常用ES6、7、8的 知识点

ES6推荐的编码风格:http://es6.ruanyifeng.com/#docs/style 值得注意

简介

ECMAScript

ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现

ES2015 (ES6)

2015年6月正式通过成为国际标准。

Babel转码器

Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行。

变量

var的时代主要有三大痛点

  1. 可重复声明
  2. 没有块级作用域
  3. 不可限制(常量、变量)

ES6 const和let的出现就是为了解决以上三个痛点。

const:声明常量

声明后不可再次修改,否则会报错。

let:声明变量

支持块级作用域

变量提升问题

  1. // var 的情况
  2. console.log(foo); // 输出undefined
  3. var foo = 2;
  4. // let 的情况
  5. console.log(bar); // 报错ReferenceError
  6. let bar = 2;

块级作用域{}

同一块级作用域{}下不可重复声明

块级作用域的出现,使得获得广泛应用的匿名立即执行函数表达式(匿名 IIFE)不再必要了。

  1. 反例:
  2. window.onload=function (){
  3. var aBtn = document.getElementsByTagName('input');
  4. for(var i=0;i < aBtn.length;i++){
  5. aBtn[i].onclick=function (){
  6. alert(i); //猜想弹出的i是多少?
  7. };
  8. }
  9. };

解构赋值

对象的解构与数组有一个重要的不同。
数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

  1. // 左边和右边 必须对应起来
  2. 对象:
  3. let {a, b, c } = {a:123, b:12313, c:35234234} // 对象解构赋值
  4. let { baz } = { foo: 'aaa', bar: 'bbb' };
  5. baz // undefined
  6. 数组:
  7. let [a, b, c] = [12, 5, 8]; // 数组解构赋值
  8. let [head, ...tail] = [1, 2, 3, 4];
  9. head // 1
  10. tail // [2, 3, 4]
  11. let [bar, foo] = [1];
  12. 以上情况都属于解构不成功,foo的值都会等于undefined

字符串的新增方法

includes(), startsWith(), endsWith()

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
  1. let url='http://www.bing.com/a';
  2. url.startsWith('http://') || url.startsWith('https://')
  3. url.startsWith('http') // true
  4. url.endsWith('a') // true
  5. url.includes('o') // true

Math 对象的扩展

Math.trunc()

Math.trunc方法用于去除一个数的小数部分,返回整数部分。

  1. Math.trunc(4.1) // 4
  2. Math.trunc(-4.1) // -4
  3. Math.trunc(-0.1234) // -0

函数的扩展

rest 参数

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

  1. function push(array, ...items) {

箭头函数

精髓

主要修复this指向的问题。
函数体内的this对象 就是定义时所在的对象,而不是之前使用时所在的对象。

简洁

  1. function(参数){ }
  2. (参数)=>{ return xxx }
  3. 如果,有且仅有1个参数,()也可以省
  4. 如果,有且仅有1条语句-return,{}可以省

数组的扩展

https://coffe1891.gitbook.io/frontend-hard-mode-interview/1/1.2.9

  • map 映射 1对1
  • forEach 遍历 循环一遍
  • filter 过滤true
    aItems.filter(item=>item.loc==cur_loc) //单个条件
    .filter(item=>item.price>=60 && item.price<100);//多个条件
  • reduce 减少 多对1
    求和、求平均数…

    Array.from()

    Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
  1. let arrayLike = {
  2. '0': 'a',
  3. '1': 'b',
  4. '2': 'c',
  5. length: 3
  6. };
  7. // ES5的写法
  8. var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
  9. // ES6的写法
  10. let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

数组实例的 find() 和 findIndex()

  • find():用于找出第一个符合条件的数组成员

    1. [1, 4, -5, 10].find((n) => n < 0)
    2. // -5
  • findIndex():返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1

    数组实例的 includes() ES7

    Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似

    1. [1, 2, 3].includes(2) // true
    2. [1, 2, 3].includes(4) // false
    3. [1, 2, NaN].includes(NaN) // true

    对象的扩展

    属性的遍历

  1. for…in: 循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
  2. Object.keys(obj):返回数组,包括对象自身的(不含继承的)所有可枚举属性的键名
  3. Object.getOwnPropertyNames(obj):返回数组,包含对象自身的所有属性的键名(不含symbol属性,但包含不可枚举属性

    比较两个值是否真相等Object.is()

  1. Object.is('foo', 'foo')
  2. // true
  3. Object.is({}, {})
  4. // false

Object.assign浅拷贝

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

  1. let ab = { ...a, ...b };
  2. // 等同于
  3. let ab = Object.assign({}, a, b);

Object.getOwnPropertyDescriptors() ES8

ES5 的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象(descriptor)。ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。

Promise 对象

是异步编程的一种解决方案,比传统的 — 回调函数和事件 更合理和更强大。

原理介绍

两大特点:

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

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

基本用法

创建一个Promise实例

  1. const promise = new Promise(function(resolve, reject) {
  2. // ... some code
  3. if (/* 异步操作成功 */){
  4. resolve(value);
  5. } else {
  6. reject(error);
  7. }
  8. });

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

  1. promise.then(function(value) {
  2. // success 状态变为resolved时调用
  3. }, function(error) {
  4. // failure 状态变为rejected时调用
  5. });

Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

  1. getJSON('/posts.json').then(function(posts) {
  2. // ...
  3. }).catch(function(error) {
  4. // 处理 getJSON 和 前一个回调函数运行时发生的错误
  5. console.log('发生错误!', error);
  6. });

Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

  1. const p = Promise.all([p1, p2, p3]);

p的状态由p1p2p3决定,分成两种情况。
(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

  1. Promise.all([
  2. $.ajax({url: 'data/1.json', dataType: 'json'}),
  3. $.ajax({url: 'data/2.json', dataType: 'json'}),
  4. $.ajax({url: 'data/3.json', dataType: 'json'}),
  5. ]).then((arr)=>{
  6. let [data1, data2, data3]=arr;
  7. console.log(data1, data2, data3);
  8. }, (res)=>{
  9. alert('错了');
  10. });

async\await ES8

ES2017标准引入async函数,使得异步操作变得更加方便。它是 Generator函数的语法糖。

  1. 内置执行器:与普通函数一样直接调用
  2. 更好的语义:async 表示函数里有异步操作、await 表示紧跟在后面的表达式需要同步(等待结果)。
  3. 更广的适用性:可以是Promise对象 和 原始类型的值(会自动转成立即resolved的Promise对象)
  4. 返回值 是 Promise:这样就可以直接适用 then来执行下一步操作了。

进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

Object.fromEntries ES10

在 JavaScript 操作中,数据在各种数据结构之间的转换都是很容易的,比如 Map 到数组、Map 到 Set、对象到 Map 等等。

  1. let map = new Map().set('foo', true).set('bar', false);
  2. let arr = Array.from(map);
  3. let set = new Set(map.values());
  4. let obj = { foo: true, bar: false };
  5. //下一句 Object.entries() 方法返回给定对象 obj 自身可枚举属性的键值对数组,
  6. //形如:[["foo",true],["bar",false]]
  7. let newMap = new Map(Object.entries(obj));

但是如果我们需要将一个键值对列表转换为对象,就要写点费劲的代码了。

  1. let map = new Map().set("foo", true).set("bar", false);
  2. let obj = Array.from(map).reduce((acc, [key, val]) => {
  3. return Object.assign(acc, {
  4. [key]: val
  5. });
  6. }, {});

该特性的目的在于为对象添加一个新的静态方法 Object.fromEntries,用于将符合键值对的列表(例如 Map、数组等)转换为一个对象。上一块的代码中的转换逻辑,现在我们只需要一行代码即可搞定。

  1. const map = new Map().set("foo", true).set("bar", false);
  2. let obj = Object.fromEntries(map);

class

基本用法

生成实例对象的传统方法是通过 构造函数,

  1. function People(name,age){
  2. this.name = name;
  3. this.age = age;
  4. }
  5. People.prototype.say=function(){
  6. console.log("hello)
  7. }
  8. People.see=function(){
  9. alert("how are you")
  10. }

ES6引入Class的概念,让 对象原型的写法更加清晰、更像面向对象编程的语法(就是一个语法糖)

  1. class People{
  2. constructor(name,age){
  3. this.name = name;
  4. this.age = age
  5. }
  6. static see(){
  7. alert("how are you")
  8. }
  9. say(){
  10. console.log("hello");
  11. }
  12. }

class 原理

继承 extends

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

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

super: 既可以 当作函数使用,也可以当作对象使用。
子类必须在constructor方法中调用super方法,否则新建实例时会报错。
super它在这里表示父类的构造函数,用来新建父类的this对象。

extends 原理

Module 模块体系

语法

在ES6之前 社区主要有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。 ES6的诞生完全取代了两者,成为 浏览器和服务器通用的模块解决方案。

特点:
es6模块的设计思想是 尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD 都只能在运行时确定这些东西。

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

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

  1. // profile.js
  2. export var firstName = 'Michael'; //1. 普通方式
  3. var year = 1958; // 2. 使用{}指定索要输出的一组变量 (推荐写法)
  4. export { year };
  5. // export-default.js
  6. export default function() {xxx} //3. 默认输出函数,其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。
  7. // import-default.js
  8. import customName from './export-default';
  9. customName(); // 'foo'

js加载实现

默认方式:

  1. <!-- 外部脚本 -->
  2. <script type="application/javascript" src="path/to/myModule.js"></script>
  3. //默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,再继续向下渲染。

ES6 方式:

  1. <script src="path/to/myModule.js" defer></script>
  2. <script src="path/to/myModule.js" async></script>
  3. // <script>标签打开defer或async属性,脚本就会异步加载。

一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

**ES6模块:浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。、

  1. <script type="module" src="./foo.js"></script>