ES6 新特性 | 青训营笔记

89 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 17 天

ES6+ 新特性

面试官通常会问这么一个问题:你都用过 ES6 的那些新特性?下面我们就来总结下这个问题。

  • let 和 const

这应该是我们使用最多的一个特性了。还记得上面说到的 JavaScript 只有函数级作用域没有块级作用域吗?那是在 ES6 之前,有了 ES6 的 let 和 const 我们声明出来的变量也有了块级作用域,如下所示:

{
  var a = "张三";
  const b = "张三";
  let c = "张三";
}
console.log(a); // 张三
console.log(b); // b is not defined
console.log(c); // c is not defined

还记得上面的那个 for 循环吗?如果我们使用 ES6 进行改造就会简单很多,如下所示:

for (const i = 0; i < 10; i++) {
  console.log(i); // 0-9
}

是不是比闭包的实现简单很多。

在使用上,let 和 const 虽然都可以用来声明变量,声明的变量都是块级作用域。但是用 const 声明的简单类型的变量不可以修改,如下所示:

const name = "张三";
name = "张三"; // 报错

let name = "张三";
name = "张三"; // 正常运行

const people = {
  love: "吃饭",
};
people.love = "学习"; // 复杂类型的变量可以修改 正常运行
  • 解构及扩展运算符

解构用法比较简单,如下所示:

const people = {
  name: "张三",
  love: "吃饭",
};
// 把name 和love 解构出来

const { name, love } = people;

console.log(name, love); // 张三 吃饭

在实际项目中,解构通常用于接口返回数据的处理,如下所示:

const data = await getInfo(); //  接口数据

const { name = "xxx", sex = 0 } = data; // 把自己想要的数据解构出来,并设置默认值

扩展运算符(...)可以理解成把对象里面的全部内容解构出来,使用如下所示:

const obj1 = {
  name: "张三",
};
const obj2 = {
  age: 18,
};
const user = { ...obj1, ...obj2 }; // { name:'张三',age:18 }
  • 函数默认参数

在 ES6 中,函数是可以设置默认参数的,如下所示:

function demo(name = "张三") {
  console.log("我是" + name);
}

值得注意的是,如果函数设置了默认值,那么函数的 length 属性将会失效,如下所示:

通过 length 可以获取函数的参数个数

function demo(name = "张三") {
  console.log("我是" + name);
}
console.log(demo.length); // 输出0 实际是有一个参数
  • Symbol

Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。Symbol 值通过 Symbol 函数生成,如下所示:

const fnName = Symbol();

typeof fnName; // "symbol"

call 函数可以用来改变函数的 this 指向,下面我们自己实现一个原生的call方法myCall来具体说明。如下所示:

const obj = {
  name: "张三",
};
const userInfo = {
  name: "李四",
  say: function (age) {
    console.log("我叫" + this.name, "年龄" + age);
  },
};

userInfo.say.call(obj, 18); // 输出我叫张三 年龄18

原本 say 函数里面的 this 指向的是 userInfo 本身,经过 call 调用后改变了 this 指向,从而指向了 obj。

下面我们自己实现一个 myCall 方法,如下所示:

Function.prototype.myCall = function (obj, ...arg) {
  // ...arg 表示后面的所以参数
  const fnName = Symbol();
  obj[fnName] = this;
  const res = obj[fnName](...arg);
  delete obj.fnName;
  return res;
};

对于新手而言,手动实现一个 myCall 函数还是有一定难度的,实现这个函数的原理就是把要改变 this 指向的函数作为对象的一个属性,这样的话在实现这个函数的时候,this 自然就会指向这个对象了。

如果看不懂上面的函数那也没关系,因为这不是我们的重点。我们需要关心的是 Symbol 的使用,在这里我们使用 Symbol 创建出了一个唯一的函数名称,这样就确保不会跟 obj 原有的名称冲突。

  • Set 和 Map 数据结构

ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值,具体使用如下所示:

const arr = new Set([1, 2, 3]);

arr.add(4); // 向arr中添加元素
arr.delete(1); // 删除数据为1的元素
arr.size; // 返回arr长度
arr.has(2); // 判断arr中是否有2这个元素
arr.clear(); // 清除所有元素

下面我们使用 Set 来实现数组去重:

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

const arr2 = [...new Set(arr)];

console.log(arr2); // [1,2,3,4]

是不是非常的简单。

Map 的使用很像对象 Object,也是通过健值的方式储存数据,不同的是 Object 中的键只能是字符串,而 Map 中的健可以是任意数据类型,如下所示:

const m = new Map();
const k = {
  name: "张三",
};
m.set(k, 18);
m.get(k); // 18

Map 的 api 和 Set 的基本一致,如下所示:

const m = new Map();

m.set("name", "张三"); // 设置元素
m.get("name"); // 张三
m.has("name"); // 判断有没有这个元素
m.size; // 获取map的长度
  • Promise

Promise 是异步编程的一种解决方案,在没有 Promise 之前,我们只能通过回调的方式实现异步编程,如下所示:

function fn(name, fn) {
  const nameVal = "我是" + name;
  //   用定时器模拟异步执行
  setTimeout(function () {
    fn(nameVal);
  }, 1000);
}

fn("张三", function (val) {
  console.log(val); // 我是张三
});

但是如果回调函数比较多的话,就会陷入回调地狱,大大降低了代码的可读性。而 Promise 就是来解决这个问题的,具体用法如下所示:

const promise = new Promise(function(resolve,reject){
    if(/*异步程序成功*/){
        resolve(res)
    }else{
        reject(error)
    }
})
promise.then(function(res){

},function(error){

})

Promise 有几个比较重要的方法,如下所示:

  1. Promise.prototype.then() Promise 实例添加状态改变时的回调函数
  2. Promise.prototype.catch() 发生错误时的回调函数
  3. Promise.all() Promise.all 可以将多个 Promise 实例包装成一个新的 Promise 实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值
  4. Promise.race() 可以将多个 Promise 实例包装成一个新的 Promise 实例,哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态

具体的使用和面试中常考的点我们会在浏览器事件循环再次讲到。以上列举的只是一些工作中我们经常用到的 API,关于 ES6 新增的其他 API 我们可以在这里进行学习。