快速上手ES6

85 阅读8分钟

前言

分享一些日常开发中常用到的ES6, 可以让初学者快速上手ES6

let,const

ES6引入的定义变量的关键字, 用于代替var关键字

  • let定义变量
  • const定义常量

var最主要的区别就是:

  • 变量未初始化时不能使用
  • 变量不可重复声明

以下为常见错误示范:

let name;
name = "张三";

// 重复声明
let name;

// 变量初始化之前不可使用
console.log(age * 10);

const age = 18;
// 修改常量
age = 20;

箭头函数

箭头函数提供更简洁的写法, 可以用来代替匿名函数

// ES5写法
const fn1 = function () { };

// ES6写法
const fn2 = () => { };


const arr = [1, 2, 3];

// 使用普通函数
arr.forEach(function(e) {
  console.log(e);
})

// 使用箭头函数
arr.forEach((e) => {
  console.log(e);
})

箭头函数里没有argumens也没有this, this的值是继承自上一级作用域的值

// 非严格模式下

window.name = "window";
const util = {
  name: "util",
  fn1: function () {
    console.log(...arguments); // 1 2 3
    console.log(this.name); // util
  },
  fn2: () => {
    console.log(this.name); // window
  }
}

util.fn1(1, 2, 3);
util.fn2(1, 2, 3);

当箭头函数的参数只有一个时可以省略小括号, 当函数体只有一行代码时, 可以省略大括号, 会默认返回该行代码的返回值

// 一个参数省略小括号
const multip = n => {
  return n * 10;
}

// 函数体一行语句, 省略大括号, 默认返回该值
const divide = n => n / 10;

交换数组的值

使用临时变量

const arr = [1, 2];
let temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;

console.log(arr); // (2) [2, 1]

ES6写法

const arr = [1, 2];
[arr[0], arr[1]] = [arr[1], arr[0]];
console.log(arr); // (2) [2, 1]

数组的合并

数组的concat()方法

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const newArr = arr1.concat(arr2, 7, 8);
console.log(newArr); // (8) [1, 2, 3, 4, 5, 6, 7, 8]

ES6展开运算符

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// 方式一
const newArr1 = [...arr1, ...arr2, 7, 8];

// 方式二
const newArr2 = [];
newArr2.push(...arr1, ...arr2, 7, 8);

console.log(newArr1); // (8) [1, 2, 3, 4, 5, 6, 7, 8]
console.log(newArr2); // (8) [1, 2, 3, 4, 5, 6, 7, 8]

展开运算符同样支持对象的合并

const obj1 = { name: "张三" };
const obj2 = { age: 18 }; 

const newObj = { ...obj1, ...obj2 };
console.log(newObj); // {name: '张三', age: 18}

无论是数组还是对象通过展开运算符合并的数据都是浅拷贝, 即只拷贝引用

伪数组转换为数组

函数的arguments(除箭头函数), document.querySelectorAll(), document.getElementsByClassName()等都是一些比较常见伪数组

伪数组: 具有数组的length属性, 按照索引进行存取数据, 不是真正意义的数组, 不具备数组的一些方法

使用Array.from()转换伪数组为真正的数组

function fn() {
  console.log(arguments.forEach); // undefined
  console.log(Array.isArray(arguments)); // false

  const restPar = Array.from(arguments);
  console.log(Array.isArray(restPar)); // true
}

fn(1, true, "hello");

ES6展开运算符

function fn() {
  const restPar = [...arguments];
  console.log(Array.isArray(restPar)); // true
}

fn(1, true, "hello");

接受不定数量的函数参数

ES5的arguments

function sum() {
  return Array.from(arguments).reduce((cur, pre) => cur + pre, 0);
}

console.log(sum(1, 2, 3, 4)); // 10

ES6展开运算符

const sum = (...args) => args.reduce((cur, pre) => cur + pre, 0);

console.log(sum(1, 2, 3, 4)); // 10

可以用展开运算符来接受剩余的参数

// 不考虑参数不足的情况
const sum = (n1, n2, ...args) => {
  console.log(n1, n2);
  return args.reduce((cur, pre) => cur + pre, n1 + n2);
}

console.log(sum(1, 2, 3, 4)); // 10

// 错误写法
// n1 和 n2 是拿不到值的
const sum2 = (...args, n1, n2) => {
  return args.reduce((cur, pre) => cur + pre, n1 + n2);
}

传递多个参数给函数

Math.max()为例, ES5可以使用apply

const arr = [1, 5, 99, 20];
const max = Math.max.apply(null, arr);
console.log(max); // 99

ES6展开运算符

const arr = [1, 5, 99, 20];
const max = Math.max(...arr);
console.log(max); // 99

函数参数默认值

ES5会去判断参数是否传递, 如未传递则手动赋默认值

function sayHi(msg) {
  msg = msg || "hello";
  alert(msg);
}

ES6写法

function sayHi(msg = "hello") {
  alert(msg);
}

结构赋值

常规写法

const obj = { code: 200, msg: null, data: [] };

const code = obj.code;
const msg = obj.msg;
const data = obj.data;

ES6写法

const obj = { code: 200, msg: null, data: [] };

// 以对象的key为匹配解构对应的值为变量
const { code, msg, data  } = obj;

结构的同时还可以对其重命名

const { code: myCode, msg: myMsg, data: myData } = obj;

数组同样可以

const arr = [200, null, []];

// 数组是根据其索引进行解构的
const [code, msg, data] = arr;

数组是根据其索引进行解构的, 所以不可以进行解构重命名

可选链操作符

日常开发中, 在读取对象或数组的一些值时, 往往需要判断值是否存在

const obj = { code: 200, msg: null, data: undefined };

const data = obj.data;
if (data && Array.isArray(data)) {
  data.forEach(e => {
    // ...
  })
}

使用可选链操作符, 语法?.,?.[xxx], 在引用为null或者undefined时, 不会引起错误, 会有短路效果, 即前一个操作失败了则不会进行后续的操作, 操作失败默认返回undefined

以下操作均不会报错

const obj = { code: 200, msg: null, data: undefined };

// ?. 
obj?.data?.forEach(e => {
  // ...
});

// ?.[xxx]
const key = "msg";
const message = obj?.[key]?.toLocaleUpperCase(); 
alert(message || "默认提示");

上面的可选链的操作大概可以理解为下面的代码

const obj = { code: 200, msg: null, data: undefined };

// ?. 
if (obj.data && typeof obj.data.forEach === "function") {
  obj.data.forEach(e => {

  })
}

// ?.[xxx]
const key = "msg";
let message;
if (obj[key]) {
  if (typeof obj[key].toLocaleUpperCase === "function") {
    message = obj[key].toLocaleUpperCase();
  } else {
    message = undefined;
  }
} else {
  message = undefined;
}
alert(message || "默认提示");

对象属性的简写

当对象的keyvalue同名时(value一般为一个变量), 可以简写为一个

const name = "张三";
const age = 18;

const obj = {
  name,
  age
}

上面的代码等价于下面


const name = "张三";
const age = 18;

const obj = {
  // key 和 value 同名了, 可以省略简写
  name: name,
  age: age
}

for...of循环

实现了键为Symbol[iterator](迭代器)的方法就可以使用for...of循环, for...of循环比ES5的forEach更加简洁, 并且支持break,continue,

const arr = [1, 2, 3, 4, 5];

// for
for (let i = 0, len = arr.length; i < len; i++) {
  console.log(arr[i]);
}

console.log("-".repeat(20));

// forEach
arr.forEach((e, i) => {
  console.log(e);
});

console.log("-".repeat(20));

// for...of
for (const item of arr) {
  console.log(item);
}

在JS中String,Array,arguments,Set,map都已经实现了迭代器

遍历对象

ES5可以使用for...in来遍历对象, 如下:

const obj = {
  name: "张三",
  age: 18
}

for (const key in obj) {
  const value = obj[key];
  // ...
}

ES6提供了三个方法可以分别获取对象的键数组(Object.keys()), 值数值(Object.values())和键值对数组(Object.entries()):

const obj = {
  name: "张三",
  age: 18
}

console.log(Object.keys(obj)); // (2) ['name', 'age']

console.log(Object.values(obj)); // (2) ['张三', 18]

console.log(Object.entries(obj)); // (2) [Array(2), Array(2)]
                                  //   0: (2) ['name', '张三']
                                  //   1: (2) ['age', 18]

利用这三个方法可以很轻松的遍历一个对象

const obj = {
  name: "张三",
  age: 18
}

// Object.keys() 
for (const key of Object.keys(obj)) {
  const value = obj[key];
  // ...
}

console.log("-".repeat(20));

// Object.values()
for (const value of Object.values(obj)) {
  // ...
}

console.log("-".repeat(20));

// Object.entries()
// 注意, 这里是 数组 + 结构
for (const [key, value] of Object.entries(obj)) {
  // ...
}

includes()

在日常开发中有的时候需要判断某个值是否等于一些值时, 会写出很多个||

const val = 2;
if (val === 1 || val === 2 || val === 3) {
  // ...
}

可以使用数组的includes()方法, 该方法会判断数组中是否包含(全等比较)给定的值, 类似indexOf()方法, 只不过是返回布尔值

const val = 1;
const arr = [1, 2, 3];
if (arr.includes(val)) {
  // ...
}

判断NaN

三种方式

const val = NaN;
console.log(Object.is(val, NaN)); // true
console.log([NaN].includes(val)); // true
console.log(window.isNaN(val)); // true

Promise

可以看之前写的一篇文章

ESModule

模块化

  • export关键字可以导出(暴露)一个模块(变量, 函数)
  • import关键字可以导入(引入)一个模块

export

新建两个模块文件config.jsutil.js

逐个暴露模块

// config.js

// export default(默认暴露) 一个模块文件只能有一个默认暴露
const FONT_SIZE = 16;
export default FONT_SIZE; // 或者这样写  export default 16

// export(分别暴露) 可以有多个
export const FONT_COLOR = "#fff";
export const TITLE = "ES6moudle";

一次性暴露模块

// util.js
const sayHi = (msg = "hello") => alert(msg);
const add = (...args) => args.reduce((pre, cur) => pre + cur, 0);
const getFileName = () => "util.js";

// 表示分别暴露 sayHi, add 和 getFileName
export {
  sayHi,
  add as sum, // 这里表示将 add 重命名为 sum 后暴露
  getFileName as default // 这里表示将 getFileName 指定为 默认暴露
}

import

引入默认暴露(变量名需和暴露时同名)

// 引入的变量名可以随便起(因为一个模块文件只有一个默认暴露)
import FONT_SIZE from "./config.js";
console.log(FONT_SIZE); // 16

import getFileName from "./util.js"
console.log(getFileName()); // util.js

如果想在<script>标签中使用ESModule, 需要加上type="module"标签属性

引入模块并重命名

import { 
  default as FONT_SIZE, // 默认暴露的模块会被保存到 default 字段中
  FONT_COLOR, // 分别暴露的名称对应即可
  TITLE as hello // 引入模块时也可以对其重命名
} from "./config.js";

console.log(FONT_SIZE); // 16
console.log(FONT_COLOR); // #fff
console.log(hello); // ES6moudle

引入模块时支持*通配符

import
  getFileName, // 引入默认暴露
  * as util // 引入所有的分别暴露的模块并重命名为 util(模块会保存到util对象中)
from "./util.js"

console.log(getFileName()); // util.js
util.sayHi("你好");
console.log(util.sum(1, 2, 3, 4)); // 10

同样的暴露模块时, 也可以使用*通配符来暴露

新建moduleExport.js文件, 如下:

// 暴露 ./config.js 模块下的所有分别暴露的模块
export * from "./config.js";
// 同理
export * from "./util.js";

引入使用

import * as allModule from "./moduleExport.js";
console.log(Object.keys(allModule)); // (4) ['FONT_COLOR', 'TITLE', 'sayHi', 'sum']

直接使用通配符*暴露的话会丢失默认暴露的模块, 解决方法可以重命名后再暴露就不会丢失了, 如下:

// 注意, 这里有一个重命名
export * as config from "./config.js";
export * as util from "./util.js";

引入使用

import { config, util } from "./moduleExport.js";

// 默认暴露的模块会保存到 default 字段中
console.log(config.default); // 16
console.log(util.sum(1, 2, 3, 4)); // 10

class

类的话楼主个人在开发中用的并不多, 可能是跟公司使用的技术栈有关吧(Vue), 下次有空再写一篇JS类的使用😁