ES6重要知识点总结

719 阅读3分钟

1.1 ES6 介绍

ECMAScript 6.0(以下简称ES6) ,是 JavaScript语言 的下一代标准,已经在2015年6月正式发布了。它的目标,是使得javaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

ES6 主要是为了解决 ES5 的先天性不足,比如 JavaScript 里并没有类的概念、变量提升、内置对象的方法不灵活、模块化实现不完善等。

1.2 let和const命令

let

  • let声明变量,没有变量提升

  • 是一个块作用域(变量和函数生效的区域)

  • 不能重复声明

const

  • const声明常量,一旦被声明,无法修改

let和const的作用

  1. 解决for循环,用var声明变量i出现变量提升的问题(用var i = 0,console出现的结果是10,实际结果应是5)

    const arr = [];
    for(let i = 0; i < 10; i++) {
      arr[i] = function() {
        return i;
      }
    };
    console.log(arr[5]());
    
  2. 不会污染全局变量

    let RegExp = 10;
    console.log(RegExp);
    console.log(window.RegExp);
    

建议:在默认情况下使用const,当你知道变量值需要被修改的情况使用let

1.3 模板字符串

使用tab间上面的反引号``,插入变量时使用${变量名}

const oBox = document.querySelector(".box");
let id = 1,
    name = 'Isaac';
let htmlStr = `
	<ul>
            <li>
                <p id = ${id}>${name}</p>
            </li>
        </ul>
`;
oBox.innerHTML = htmlStr;

优点:解决了拼接字符串繁琐的操作

用途:后端文本字符串追加到前端页面

1.4 函数之默认值、剩余参数

默认值

  1. 带默认值的函数

    //es5的写法
    function add(a, b) {
      a = a || 10;
      b = b || 20;
      return a + b;
    }
    console.log(add());
    
    //es6的写法
    function add(a = 20, b = 10) {
      return a + b;
    }
    console.log(add());
    
  2. 默认的表达式也可以是一个函数

    function add(a, b = getVal(5)) {
      return a + b;
    }
    
    function getVal(val) {
      return val + 5;
    }
    console.log(add(10));
    

剩余参数

由三个...和一个紧跟着的具名参数指定 ,例如: ...keys

//es5写法
function pick(obj) {
  let result = Object.create(null);
  for(let i = 1; i < arguments.length; i ++) {
    result[arguments[i]] = obj[arguments[i]]
  }
  return result;
}
        
let book = {
  title: '新华字典',
  author: 'Isaac',
  year: 2020
}

let bookData = pick(book, 'title', 'year', 'author');
console.log(bookData);

ps:pick函数在遍历book对象并获取数据时,需要用到arguments(--[]实参列表,伪数组)的length来作为限制循环的条件。那么这时候问题来了,arguments作为此函数的实参列表,所存储的是该函数中所有的参数:arguments = [obj, 'title', 'year', 'author'],可以看出,obj其实并不是我们想要的,所以我们在遍历时要从下标为1的title开始,也就是i = 1,作为循环的起始条件。

es6 为我们提供了剩余函数的概念,应用这一概念可以很好的解决arguments的问题,如下:

function pick(obj, ...keys) {
  //...keys 解决了arguments的问题
  let result = {};
  for(let i = 0; i < keys.length; i++) {//循环的起始条件i = 0
    result[keys[i]] = obj[keys[i]];
  }
  return result;
}

let book = {
  title: '新华字典',
  author: 'Isaac',
  year: 2020
}

let bookData = pick(book, 'year', 'author');
console.log(bookData);

1.5 函数之扩展运算符、箭头函数

扩展运算符

  • 剩余运算符 vs 扩展运算符

剩余运算符:把多个独立的合并到一个数组中

扩展运算符:将一个数组分割,并将各个项作为分离的参数传给函数

//es5写法,处理数组中的最大值,使用apply
const arr = [10, 20, 50, 30, 90, 100, 40];
console.log(Math.max.apply(null, arr));

//es6写法(应用扩展运算符)
console.log(Math.max(...arr))

箭头函数

es6使用 => 来定义 function(){} 等于 () => {}

优点:使代码更简洁

//es5写法
let add = function(a, b) {
  return a + b;
}

//es6写法(使用箭头函数)
//两个形参
let add = (a, b) => {
  return a + b;
}
//或写成一行
let add = (a, b) => a + b;

//一个形参
let add = val => {
	return val + 5;
}

//无参
let fn = () => 'hello world' + 123;

//以对象为函数参数时
let getObj = id => {
	return {
    id : id,
    name : 'Isaac'
  }
}
//或写成一行,注意参数为对象时,要使用{}
let getObj = id => ({id: id, name: 'Isaac'});

//闭包函数
//es5写法
let fn = (function () {
  return function () {
    console.log('hello es6')
  }
})();

//es6写法
let fn = (() => {
  return () => {
    console.log('hello es6 2');
  }
})();
fn();

1.6 箭头函数this指向和注意事项

箭头函数的this指向

箭头函数没有this绑定,es5中this指向:取决于调用该函数的上下文

//es5写法
let PageHandle = {
  id: 123,
  init: function () {
    document.addEventListener('click', function(event) {
      //报错:this.doSomeThings is not a function
      this.doSomeThings(event.type);
    }.bind(this),false)//可以通过.bind(this)来解决this指向问题
  },
  doSomeThings:function(type) {
    console.log(`事件类型:${type},当前id:${this.id}`);
  }
}
PageHandle.init();

此时在浏览器中运行代码,点击文档,console中会报错,原因是当前的this指向init这个函数中为它定义的执行期上下文对象(document)并不是我们想要的PageHandle,所以我们可以使用es6中的箭头函数来解决这个问题,如下:

let PageHandle = {
  id: 123,
  init: function () {
  //箭头函数没有this指向,箭头函数内部this只能通过查找作用域链来确定,一旦使用箭头函数,当前不存在作用域链 
    	document.addEventListener('click', (event) => {
        this.doSomeThings(event.type);
      }, false)
  },
  doSomeThings: function (type) {
    console.log(`事件类型:${type},当前id:${this.id}`);
  }
}
PageHandle.init();

注意事项

  1. 使用箭头函数 函数内部没有arguments
  2. 箭头函数不能使用new关键字来实例化对象

ps:function函数是一个对象,但是箭头函数不是一个对象,它其实就是一个语法糖

1.7 解构赋值

  • 解构赋值是对赋值运算符的一种扩展
  • 它针对数组和对象来进行操作
  • 优点:代码书写上简洁易读
//es5写法
let node = {
   type: 'iden',
   name: 'foo'
}
let type = node.type;
let name = node.name;

//es6写法
//完全解构
let {type, name} = node;

//不完全解构
let obj = {
  a:{name: "张三"},
  b:[],
  c:'hello, world'
}

let {a} = obj;
console.log(a);
let {a, ...res} = obj;
console.log(res);

//默认值
let {a, b = 30} = {a: 20};

//对数组解构
let arr = [1, 2, 3];
let [a, b, c] = arr;
console.log(a, b, c);
//可嵌套
let [a, [b]] = [1,[2],3]

1.8 对象的扩展功能

es6直接写入变量和函数,作为对象的属性和方法

const name = 'Isaac',
      age = 20;
const person = {
  name, //等价于es5写法中的 name: name
  age,
  sayName() {
    console.log(this.name);
  }
}
person.sayName();

对象的方法

  • is() === 用于比较两个值是否严格相等
  • assign() 用于对象的合并,用法:Object.assign(target, obj1, obj2...)

1.9 Symbol类型

原始数据类型Symbol,它表示是独一无二的值

最大的用途:用来定义对象的私有变量

const name = Symbol('name');
const name2 = Symbol('name');
console.log(name === name2);

ps:上述代码可Chrome的控制台中输出结果为false,可以证明Symbol数据类型的变量是独一无二的

let s1 = Symbol('s1');
let obj = {
       [s1]:'Isaac'
};
console.log(obj[s1]);

ps:如果用Symbol定义的对象中的变量,取值时一定要用[变量名]

//获取Symbol声明的属性名(作为对象的key)
let s = Object.getOwnPropertyDescriptor(obj);
let m = Reflect.ownKeys(obj);

1.10 Set集合数据类型

集合:无重复值的有序列表

let set = new Set();
//添加元素
set.add(2);
set.add('4');
set.add(['hello', 'world', 3]);
//删除元素
set.delete(2);
//校验某个值是否在set中
console.log(set.has('4'));
//将set转换成数组
 let set2 = new Set([1, 2, 3, 3, 3, 4])
//扩展运算符
let arr = [...set2];
console.log(arr);

ps:set中对象的引用无法被释放

let set3 = new Set(),
    obj = {};
set3.add(obj);
//释放当前的资源
obj = null;
console.log(set3);

解决这个问题,可以使用WeakSet()来代替Set()集合

let set4 = new WeakSet(), 
    obj = {};
set4.add(obj);
//释放当前的资源
obj = null;
console.log(set4);

weakSet

  1. 不能传入非对象类型的参数
  2. 不可迭代
  3. 没有forEach()
  4. 没有size属性

1.11 Map数据类型

Map类型是键值对的有序列表,键和值是任意类型

let map = new Map();
map.set('name', 'Isaac');
map.set('age',20);
console.log(map.get('name'));
console.log(map);
map.has('name'); //true
map.delete('name');
map.clear();
console.log(map);

1.12 数组的扩展功能

  • from

将伪数组转换成真正的数组

function add() {
    console.log(arguments);
    //es5转换
    //let arr = [].slice.call(arguments);
    //console.log(arr);

    //es6写法
    let arr = Array.from(arguments);
    console.log(arr);
    //或者使用扩展运算符 将伪数组转换成真正的数组
    console.log([...arr]);
}

//from() 还可以接收第二个参数,用来对每个元素进行处理
let liContents = Array.from(lis, ele => ele.textContent);
console.log(liContents);
  • of

将任意的数据类型,转换成数组

console.log(Array.of(3, 11, 20, '30'));
  • copyWithin

数组内部将指定位置的元素复制到其它的位置,返回当前数组

//从3位置往后的所有数值,替换从0位置往后的三个数值
[1, 2, 3, 8, 9, 10].copyWithin(0, 3);
  • find

找出第一个符合条件的数组成员

let num = [1, 2, -10, -20, 9, 2].find(n => n < 0);
  • findIndex

找出第一个符合条件的数组成员的索引

let numIndex = [1, 2, -10, -20, 9, 2].findIndex(n => n < 0);
  • entries、keys、values

返回一个遍历器 可以使用for...of 循环进行比遍历

//keys() 对键名遍历
//values() 对值遍历
//entries() 对键值对遍历
for (let index of ['a', 'b'].keys()) {
	console.log(index);
}

for (let ele of ['a', 'b'].values()) {
  	console.log(ele);
}

for (let [index, ele] of ['a', 'b'].entries()) {
        console.log(index, ele);
}
  • includes

返回一个布尔值,表示某个数组是否包含给定的值

console.log([1, 2, 3].includes(2));
//用来代替之的indexOf
console.log([1, 2, 3].indexOf('2'));

1.13 迭代器Iterator的用法

Iterator是一种新的遍历机制,两个核心

  1. 迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator来创建迭代器 通过迭代器的next()获取迭代之后的结果
  2. 迭代器是用于遍历数据结构的指针(数据库的游标)
// 使用迭代
const items = ['one', 'two', 'three'];
// 1. 创建新的迭代器
const ite = items[Symbol.iterator]();
console.log(ite.next()); //{value: "one", done: false} done如果为false表示遍历继续 如果true表示遍历完成
console.log(ite.next());
console.log(ite.next());

1.14 生成器Generator

Generator函数 可以通过yield关键字,将函数挂起;为了改变执行流提供了可能,同时为了做异步编程也提供了方案

与普通函数的区别:

  1. function 后面函数名之前有个 *
  2. 只能在函数内部使用yield表达式,让函数挂起
function* func() {
  console.log('one');
  yield 2;
  console.log('two');
  yield 3;
  console.log('end');
}
// 返回一个遍历器对象 可以调用next()
let fn = func();
console.log(fn.next());
console.log(fn.next());
console.log(fn.next());
//总结:generator函数是分段执行的,yield语句是暂停执行 而next()恢复执行

//Example
function* add() {
  console.log('start');
  //x 可真不是yield '2'的返回值,它是next()调用 恢复当前yield()执行传入的实参
  let x = yield '2';
  console.log('one:' + x);
  let y = yield '3';
  console.log('two:' + y);
  return x + y;
}

const fn = add();
console.log(fn.next());//{value: "2", done: false}
console.log(fn.next(20));//{value: "3", done: false}
console.log(fn.next(30));//{value: 50, done: true}

// 使用场景:为不具备Interator接口的对提供了遍历操作
function* objectEntries(obj) {
  //获取对象的所有的key保存到数组[name, age]
  const propKeys = Object.keys(obj);
  for (const propkey of propKeys) {
    yield [propkey, obj[propkey]]
  }
}

const obj = {
  name:'Isaac',
  age:18
}

obj[Symbol.iterator] = objectEntries;
console.log(obj);

for(let [key, value] of objectEntries(obj)) {
  console.log(`${key},${value}`);
} 

应用:部署ajax操作,让异步代码同步化

1.15 Promise的基本使用及方法

相当于一个一个容器,保存着未来才会结束的事件(异步操作)的一个结果

各种异步操作都可以用同样的方法进行处理(eg:axios)

  • 特点
  1. 对象的状态不受外界影响,处理异步操作有三个状态:pending(进行中),resolved(成功),rejected(失败)
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果
//promise被创建时,会有一个回调函数作为他的参数
let pro = new Promise(function (resolved, rejected) {
  //回调函数有两个参数:resolved,rejected
  //执行异步操作
  let res = {
    code: 201,
    data:{
      name:'Isaac'
    },
    error:"失败了"
  }
  //设置定时器
  setTimeout(() => {
    if(res.code === 200) {
      resolved(res.data);
    }else {
      rejected(res.error);
    }
  }, 1000)
})
console.log(pro);

//应用then()方法来接收Promise构造函数处理异步的结果,then()有两个参数
pro.then((val) => {
  //接收成功的结果
  console.log(val);
}, (err) => {
  //接收失败的结果
  console.log(err);
});

自己封装ajax,如下:

const getJSON = function(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onreadystatechange = handler;
    xhr.responseType = 'json';
    xhr.setRequestHeader('Accept', 'application/json');
    //发送
    xhr.send();
     
    function handler() {
      if(this.readyState === 4) {
        if(this.status === 200) {
          resolve(this.response.HeWeather6);
        }else {
          reject(new Error(this.statusText));
        }
      }
    }
  })
}

getJSON('https://free-api.heweather.net/s6/weather/now?location=beijing&key=b1eac8ade8b749bfb154b194a06964a4')
  .then((data) => {
   console.log(data);
 },(error) => {
   console.log(error);
 })
  • then

有两个参数,第一个参数是resolve回调函数,第二个参数是可选的,是reject状态回调的函数,返回一个新的Promise实例,可以采用链式编程

  • resolve与rejected

能将现有的任何对象转换成Promise对象

let p = new Promise(resolve => resolve('foo'));
p.then((data) => {
  console.log(data);
})
  • all

应用:一些游戏类的素材比较多,等待图片、flash、静态资源文件都加载完成才进行页面的初始化

let promise1 = new Promise((resolve, reject) => { });
let promise2 = new Promise((resolve, reject) => { });
let promise3 = new Promise((resolve, reject) => { });

let p4 = Promise.all([promise1, promise2, promise3]);
p4.then(() => {
  //三个都成功 才成功
}).catch(err => {
  //如果有一个失败则失败
})
  • race

用于某个异步请求设置超时时间,并且在超时后执行相应的操作

//请求图片资源
function requestImg(imgSrc) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = function() {
      resolve(img);
    }
    img.src = imgSrc;
  })
}

function timeOut() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('图片请求超时'));
    }, 50);
  })
}

Promise.race([requestImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1594914423649&di=c26d75c17b18f01d8cc7e6db0886eb61&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg'),timeOut()])
  .then(data => {
  console.log(data);
  document.body.appendChild(data);
}).catch(err => {
  console.log(err);
});

1.16 async的用法

作用使得异步操作更加方便

基本操作:async会返回一个Promise对象,then(),catch()

ps:async是Generator的一个语法糖

async function f() {
  let s = await 'hello async';
  let data = await s.split('');
  return data;
}
//如果async函数中有多个await 那么函数会等待所有的await指令运行完的结果 才去执行
f().then(v => {
  console.log(v);
}).catch(e => {
  console.log(e);
})

1.17 class类

//es5 造类
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayName = function() {
  return this.name;
}

let p1 = new Person('Isaac', 19);
console.log(p1);

//es6
class Person {
  //实例化的时候会立即被调用
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}
//通过Object.assign()方法一次性向类中添加多个方法
Object.assign(Person.prototype, {
  sayName() {
    return this.name;
  },
  sayAge() {
    return this.age;
  }
})
let p1 = new Person('Isaac', 28);
console.log(p1);

ps:可以使用关键字extends实现子类对父类的继承

1.18 module模块化实现

  • es6 模块功能主要由两个命令构成:import 和 export

  • export 用于规定模块的对外接口 import用于输入其它模块提供的功能

  • 一个模块就是独立的文件