我是经过阮一峰老师的ES6教程入门的,基本上是把ES6的几个核心特性过了一遍,可是面试官一问深我就???了,仍是实际运用的太少。javascript
本篇文章也偏总结类,结合我亲身经历的高频面试题,建议你们必需要对箭头函数、Promise、Generator、async等内容深刻理解。前端
工具:Babel是一个 ES6 转码器,能够将 ES6 代码转为 ES5 代码,以便兼容那些还没支持ES6的平台。java
新增了字符串模板,在拼接大段字符串时,用反斜杠取代以往的字符串相加的形式,能保留全部空格和换行,使得字符串拼接看起来更加直观,更加优雅。es6
新增了includes()方法,用于取代传统的只能用indexOf查找包含字符的方法, 此外还新增了startsWith(), endsWith(), padStart(),padEnd(),repeat()等方法,可方便的用于查找,补全字符串。面试
数组解构赋值: 如ES6能够直接以let [a,b,c] = [1,2,3]
形式进行变量赋值,映射关系更清晰。编程
扩展运算符:json
console.log(...[1, 2, 3]) // 1 2 3
复制代码
能够实现数组的复制和解构赋值 (let a = [2,3,4]; let b = [...a])
数组
能够取代arguments对象和apply方法,轻松获取未知参数个数状况下的参数集合。promise
使用扩展运算符替代函数的apply方法:bash
// ES5 的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6 写法
let args = [0, 1, 2];
f(...args);
复制代码
JS中遍历数组的方法:
myArray.forEach(function(value){
console.log(value);
})
复制代码
没法中途跳出forEach循环,break命令或return命令都不能奏效。
for... in
for…in主要是为遍历对象而设计的,不适用于遍历数组。
遍历数组的缺点:
for...of
(ES6)for(let value of myArray){
console.log(value);
}
复制代码
for of 和 for in 的总结:
ES6能够直接以变量形式声明对象属性或者方法。比传统的键值对形式声明更加简洁,更加方便,语义更加清晰。
let [apple, orange] = ['red appe', 'yellow orange'];
let myFruits = {apple, orange};
// let myFruits = {apple: 'red appe', orange: 'yellow orange'};
复制代码
可将一个数组转为用逗号分隔的参数序列,主要用于函数调用。 console.log(...[1, 2, 3]) // 1 2 3
ES6在Class类里新增了相似this的关键字super。同this老是指向当前函数所在的对象不一样,super关键字老是指向当前函数所在对象的原型对象。
基本使用:
若是 return 值就只有一行表达式,能够省去 return,默认表示该行是返回值,不然须要加一个大括号和 return。若是参数只有一个,也能够省去括号,两个则须要加上括号。
var f = v => v*2;
// 等价于
var f = function(v){
return v*2;
}
// 判断偶数
var isEven = n => n % 2 == 0;
// 须要加 return
var = (a, b) => {
if(a >= b)
return a;
return b;
}
复制代码
ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不须要使用arguments对象了。
rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
//利用 rest 参数,能够向该函数传入任意数目的参数。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
复制代码
面试问题:箭头函数和普通函数的区别
函数中的 this 始终是指向函数执行时所在的对象。好比全局函数执行时,this 指向 window,对象的方法执行时,this 指向该对象,这就是函数 this 的可变。
而箭头函数中的 this 是固定的,箭头函数继承本身做用域的上一层的this,就是上一级外部函数的 this 的指向。任何方法都改变不了其指向,如call(), bind(), apply()。
一个例子:
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 }); // id: 42
复制代码
执行的结果是 42 而不是全局的 21,表示 setTimeout 函数执行的时候,this 指向的不是 window。所以箭头函数很好地解决了匿名函数和setTimeout
和setInterval
的this指向问题,不用再去给其用that变量存储this。
对箭头函数中关于 this 的总结:在对象的方法中直接使用箭头函数,会指向 window,其余箭头函数 this 会指向上一层的 this,箭头函数并无存储 this。
var obj = {
id: 1,
foo: ()=>{
return this.id;
}
}
var id = 2;
obj.foo(); // 2
复制代码
arguments
, 取而代之用rest参数(形式为...变量名)。也没有super
、new.target
。yield
命令,箭头函数不可用做Generator
函数。ES6引入的一种相似Array的新的数据结构,Set实例的成员相似于数组item成员,区别是Set实例的成员都是惟一,不重复的。这个特性能够轻松地实现数组去重。
Set自己是一个构造函数,用来生成 Set 数据结构。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
复制代码
JavaScript 的对象(Object),本质上是键值对的集合,可是传统上只能用字符串看成键。这给它的使用带来了很大的限制。
Map
是ES6引入的一种相似Object的新的数据结构,也是键值对的集合,可是“键”的范围不限于字符串,各类类型的值(包括对象)均可以看成键。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
复制代码
没有块级做用域回来带不少难以理解的问题,好比for循环var变量泄露,变量覆盖等问题。
let
声明的变量拥有本身的块级做用域,形如for (let x...)的循环在每次迭代时都为x建立新的绑定。且修复了var声明变量带来的变量提高问题。(必须声明 'use strict' 后才能使用let声明变量,不然浏览并不能显示结果)
“变量提高”现象:即变量能够在声明以前使用,值为undefined。为了纠正这种现象,let命令改变了语法行为,它所声明的变量必定要在声明后使用,不然报错。
ES5 只有全局做用域和函数做用域,没有块级做用域。
块级做用域的出现,实际上使得得到普遍应用的当即执行函数表达式(IIFE)再也不必要了。
// IIFE 写法
(function () {
var tmp = ...;
...
}());
// 块级做用域写法
{
let tmp = ...;
...
}
复制代码
const
声明一个只读的常量。一旦声明,常量的值就不能改变。 const 声明的变量不得改变值,这意味着,const一旦声明变量,就必须当即初始化。
const的做用域与let命令相同:只在声明所在的块级做用域内有效。
总结: 使用var声明的变量,其做用域为该语句所在的函数内,且存在变量提高现象; 使用let声明的变量,其做用域为该语句所在的代码块内,不存在变量提高; 使用const声明的是常量,在后面出现的代码中不能再修改该常量的值。
主要做用是用来解决JS回调机制产生的“回调地狱”。 回调地狱带来的负面做用有如下几点:
Promise它不是新的语法功能,而是一种新的写法,将回调函数的嵌套,改为链式调用。
new Promise(请求1)
.then(请求2(请求结果1))
.then(请求3(请求结果2))
.catch(处理异常(异常信息))
复制代码
Promise 使用总结:
而后调用上一步返回的 promise 对象的 then 方法,注册回调函数。 then 中的回调函数能够有一个参数,也能够不带参数。若是 then 中的回调函数依赖上一步的返回结果,那么要带上参数。
最后注册 catch 异常处理函数,处理前面回调中可能抛出的异常。
简单例子:
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}
timeout(100).then((value) => {
console.log(value);
});
复制代码
timeout方法返回一个Promise实例,表示一段时间之后才会发生的结果。过了指定的时间(ms参数)之后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。
一些经常使用API:
Promise.race
类方法,多个 Promise 任务同时执行,返回最早执行结束的 Promise 任务的结果,无论这个 Promise 结果是成功仍是失败。
Promise.all
类方法,多个 Promise 任务同时执行。 若是所有成功执行,则以数组的方式返回全部 Promise 任务的执行结果。 若是有一个 Promise 任务 rejected,则只返回 rejected 任务的结果。
若是后续任务是异步任务的话,必须return 一个 新的 promise 对象。若是后续任务是同步任务,只需 return 一个结果便可。
new Promise(买菜)
//用买好的菜作饭
.then((买好的菜)=>{
return new Promise(作饭);
})
复制代码
一个 Promise 对象有三个状态,而且状态一旦改变,便不能再被更改成其余状态:
generator 以及 async/await 语法使异步处理更加接近同步代码写法,可读性更好,同时异常捕获和同步代码的书写趋于一致。
(async ()=>{
let 蔬菜 = await 买菜();
let 饭菜 = await 作饭(蔬菜);
let 送饭结果 = await 送饭(饭菜);
let 通知结果 = await 通知我(送饭结果);
})();
复制代码
Generator 函数会返回一个遍历器对象,能够依次遍历 Generator 函数内部的每个状态。
形式上,Generator 函数是一个普通函数,可是有两个特征。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
复制代码
Generator 函数的调用方法与普通函数同样,也是在函数名后面加上一对圆括号。不一样的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象。
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法能够恢复执行。
ES2017 标准引入了 async 函数,使得异步操做变得更加方便。
async 函数就是 Generator 函数的语法糖。
async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await。而且返回一个
Promise
,可使用then方法添加回调函数。 当函数执行的时候,一旦遇到await就会先返回,等到异步操做完成,再接着执行函数体内后面的语句。
async函数对 Generator 函数的改进:
例子:getJSON函数返回一个promise,这个promise成功resolve时会返回一个json对象。咱们只是调用这个函数,打印返回的JSON对象,而后返回”done”。
// promise
const makeRequest = () =>
getJSON()
.then(data => {
console.log(data)
return "done"
})
makeRequest()
复制代码
//使用Async/Await
const makeRequest = async () => {
console.log(await getJSON())
return "done"
}
makeRequest()
//async函数会隐式地返回一个promise,该promise的reosolve值就是函数return的值。(示例中reosolve值就是字符串”done”)
复制代码
Async的优缺点:
优点: 处理 then 的调用链可以更清晰准确的写出代码。
缺点: 滥用 await 可能会致使性能问题,由于 await 会阻塞代码,也许以后的异步代码并不依赖于前者,但仍然须要等待前者完成,致使代码失去了并发性。
是ES6中一个很重要概念,它并非对象,也不是任何一种数据类型。为Set、Map、Array、Object新增一个统一的遍历API。部署了Iterator接口的对象(可遍历对象)均可以经过for...of
去遍历。
ES6 的class能够看做只是一个语法糖,它的绝大部分功能,ES5 均可以作到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,能够看作是构造函数的另外一种写法。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
//ES6的class改写
class Point {
//构造方法
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
复制代码
上面代码定义了一个“类”,能够看到里面有一个constructor方法,这就是构造方法,而this关键字则表明实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的Point类的构造方法。
Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不须要加上function这个关键字,直接把函数定义放进去了就能够了。另外,方法之间不须要逗号分隔,加了会报错。
class实现继承: Class 能够经过extends关键字实现继承,这比 ES5 的经过修改原型链实现继承,要清晰和方便不少。
平常前端代码开发中,有哪些值得用ES6去改进的编程优化或者规范:
var self = this;
的作法。