这是我参与「第五届青训营 」伴学笔记创作活动的第 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 有几个比较重要的方法,如下所示:
Promise.prototype.then()Promise实例添加状态改变时的回调函数Promise.prototype.catch()发生错误时的回调函数Promise.all()Promise.all 可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值Promise.race()可以将多个Promise实例包装成一个新的Promise实例,哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态
具体的使用和面试中常考的点我们会在浏览器事件循环再次讲到。以上列举的只是一些工作中我们经常用到的 API,关于 ES6 新增的其他 API 我们可以在这里进行学习。