本篇文章面试官问到es6时,你可以按照套路一一回答面试官的问题,给面试官你基础还不错的印象。
请问你熟悉哪些ES6新特性?
- let和const
- 变量的解构赋值
- 字符串、数组、函数、对象、运算符拓展
- Symbol
- Proxy
- Reflect
- Promise对象、async函数、Genertor函数语法
- Iterator和for...of循环
- class的基本语法
- class继承
- Module的语法
- Moule的语法
- Moudle的加载实现
- 区别篇
1. 请问let,var,const区别?
块级作用域方面: let和const有块级作用域,var没有块级作用域,但有函数作用域
变量提升方面:var支持变量提升,const和let不支持,且let会出现暂时性死区的问题
声明赋值方面:var可以重复声明,const和let不能重复声明,且const一旦初始化数据就不能赋值否则报错。
2. 箭头函数和普通函数的区别?
- 箭头函数没有自己的
this对象,它的this是它执行上下文的this - 不可以当作
构造函数,也就是说不能new,否则会抛出一个错误 - 没有
argument - 不可以使用
yield命令,因此箭头函数不能用作Generator函数。
3. 介绍下Set、Map、WeakSet和WeakMap的区别?
- Set
它类似于数组,成员的值是唯一的,没有重复的值,set本身是一个构造函数,用于生成Set数据结构,主要的方法有add、delete、has,可以遍历,应用场景数组去重。
- weakSet
它与Set类似,也是不重复的值的集合。
它与Set的区别是:
WeakSet的成员只能是对象,而不能是其他类型的值。
- 没有
size属性,不能遍历
WeakSet中对象都是弱引用,垃圾回收机制考虑WeakSet对该对象的引用,也就是说,如果其他对象都不在引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑对象还存在WeakSet之中。应用场景:WeakSet适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在WeakSet里面的引用也会消失,这样不容易造成内存泄漏
- Map
本质是键值对的集合(hash结构),键可以是对象,优化了Object。主要的方法为get、set,has、delete,可以遍历。可以干各种类型转换。
- WeakMap
它与Map类似,它和Map的区别:
- 键名只接受对象
- 没有
size属性,不能遍历
- 是弱引用,不被垃圾回收机制回收
面试官:如何理解垃圾回收机制?(填坑)谈工作原理即可
工作原理:垃圾回收机制依赖引用计数,如果一个值不为0,垃圾回收机制就不会释放这块内存。结束使用该值,就会释放该值内存。如果忘记取消引用,就会导致内存无法释放,进而可能引起内存泄漏。
4. async await对比promise的优缺点?
async/await优点:
- 它做到真正的串行的
同步写法,代码阅读更简单 - 对于条件语句和其他流程比较
友好,可以直接写到判断条件
function a() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111)
}, 1111)
})
};
async function f() {
try {
if ( await a() === 111) {
console.log('yes, it is!') // 会打印
}
} catch (err) {
// ...
}
}
- 处理复杂流程时,在代码
清晰度方面有优势
async/await缺点:
无法处理promise返回对象的reject对象,要借助try...catch...- 用await可能会导致性能问题,因为
await会阻塞代码,之后的代码也许不依赖于前者,但仍然需要等待前者的完成,导致代码失去并发性。 - try...catch...内部的变量无法传递给下一个try...catchPromise和then/catch内部定义的变量
promise的一些问题:
- 一旦执行,就
无法中途取消,链式调用多个then不能随便跳出来 - 错误
无法在外部捕捉,只能在内部预判处理,如果内部进行预判处理,如果不设置回调函数,Promise内部抛出的错误,不能反应到外部 - Promise内部如何执行,检测很难,当处于pending状态时,无法得知目前进展到哪一个阶段
5. 简述一下Promise,async&await两者者的区别?
- promise通过链式调用,直接在then中返回一个promise来进行成功之后的回调函数,用catch来做错误处理
- async是
Generator函数的语法糖,async/await则将其变成同步的写法,即可以用try-catch捕获,简洁,可读性更高,写法更优雅
6. CommonJS和ES6模块的区别
CommonJS模块输出是一个值的拷贝(浅拷贝),ES6模块输出是值的引用CommonJS是运行时加载,ES6模块是编译时输出接口CommonJS模块的require()是同步加载模块,ES6模块的import命令是异步加载,有一个独立依赖的解析模块阶段
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值
ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
补充点:可能面试官会问你还见过哪些规范
就可以说一下AMD和CMD
AMD规范的模块化:用 require.config()指定引用路径等,用define()定义模块,用require()加载模块。
CMD规范的模块化:用define()定义模块, seajs.use 引用模块。
- 字符、数组、对象、函数、运算符拓展篇
1. 你是如何理解数组对象内容解构?
你可以举如下几个例子:
- 数组结构
let [a1,a2,a3] = [1,2,3] //a1 = 1; a2 = 2; a3 = 3;
- 对象解构
let {name,age} = {name:'meteor', age:8} // name = 'meteor' age = 8
- 复杂解构
let [{age}] = [{age:8,name:'xx'},'江西',[1,2,3]] //age = 8 注意对象解构
- 默认赋值
let {age = 5} = {age:8,name;'xx'} //age = 8 如果没有age字段age = 5
- 常用函数给默认参数
//以前
function(){var a = a || 5}
//现在
function(a = 5){}
2. 请问字符串增添哪些方法?
- 反引号(可以拼接一些变量)
let name = 'kk';
let age = 20;
let str = `${name}${age}岁了`
console.log(str); //kk20岁了
- includes方法
//判断字符串是否包含某个字符串
let str = '23123S';
str.includes('3S')//true
- endsWith、startsWith方法
//判断字符串是否以某一个字符串开始或结束
var a = '1AB42342D'
console.log(a.startsWith('1A')) //true
console.log(a.endsWith('2d')) //false
3. 数组新增哪些方法,并指明它的用法?
-
Array.from()将类数组转化数组(对象,Set,Map) -
Array.of()用于将一组值,转换为数组
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
Array.fill()填充数组Array.find()和findIndex()
[1, 4, -5, 10].find((n) => n < 0)
// -5
[1, 4, 5, 10].find((n) => n < 0) //undefined
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 15;
}) // -1
entries(),keys()和values()遍历数组includes()表示某个数组是否包含给定的值
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true
flat(n)n默认为1,表示拉平次数
//数组扁平化流氓解法
[1, [2, [3]]].flat(Infinity)
flatMap()只能展开一层数组
// 相当于 [[[2]], [[4]], [[6]], [[8]]].flat()
[1, 2, 3, 4].flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]
4. 请问对象的拓展哪些属性?
- super关键字
this关键字总是指向函数所在的当前对象,而super指向当前对象的原型对象
const proto = {
foo: 'hello'
};
const obj = {
foo: 'world',
find() {
return super.foo;
}
};
Object.setPrototypeOf(obj, proto);
obj.find() // "hello"
- 遍历方式
- fo...in
- Object.keys()
- Reflect.ownKeys(obj)
返回一个数组
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]
- 新增方法
-
Object.is方法判断两个值是否同一个值 -
Object.assign方法用于对象的合并,复制到目标对象,且是浅拷贝
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
5. 请问函数有哪些拓展呢?
- rest参数(
...变量名)
// arguments变量的写法
function sortNumbers() {
return Array.from(arguments).sort();
}
// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();
- name属性
function foo() {}
foo.name // "foo"
- 箭头函数(常问普通函数的区别)
- 严格模式
只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
6. 请问你见过的运算符拓展有哪些?
指数运算符(**)
2 ** 2 // 4
2 ** 3 // 8
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
看更多拓展可以参考链接运算符的扩展 - ECMAScript 6入门 (ruanyifeng.com)
- class继承篇
1. 请聊一下class继承
- class可以通过
extends关键字实现继承 Object.getPrototypeOf方法可以用来从子类上获取父类。super这个关键字,,既可以当作函数使用,也可以当作对象使用。- 子类的
__ proto __属性,表示构造函数继承,总是指向父类 - 子类的
prototype属性的__ proto __属性,表示方法的继承,总是指向父类的prototype属性 - Mixin模式实现了继承多个类,参照以下写法
class DistributedEdit extends mix(Loggable, Serializable) {
// ...
}
- promise篇(高频)
1. 请简述下promise的优缺点?
缺点:
- 无法取消Promise,一旦新建就会立即执行,无法中途取消
- 如果不设置回调函数,promise内部抛出的错误就无法反应到外部
- 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
优点:
- 解决回调地狱问题
- 代码扁平可读,.then方法链式调用
- 更好的进行错误捕获
2. 请聊一下promise.then() .catch() .finally()?
- 首先三者多是微任务
.then、.catch、.finally都会返回一个新的Promise.then方法能接受两个参数,第一个是处理成功的函数,第二个处理失败的函数- catch不管连接到哪一层,都能捕获上传未捕捉的错误。
.finally的回调函数不接受任何参数,也就是没法知道最终状态是resolved还是rejected的
3. 请聊下promise.all() promise.race()和promise.any()
promise.all:
- 同时处理多个promise对象,包装到一个新的Promise实例(常会被问到),如果传递的参数不是promise实例,他会调用promise.resolve方法,将参数转换实例
举个例子,p1,p2,p3都是promise对象
const p = Promise.all([p1, p2, p3]);
其中p状态由p1,p2,p3,分成两种情况
- 只有p1,p2,p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值成一个数组,传递给
p的回调函数 - 只要p1,p2,p3之中有一个被rejected,p的状态就变成rejected的实例返回值,会传递给p的回调函数
promise.race:
- 同时处理多个promise实例,包装成一个新的Promise实例,如果传递的参数不是promise实例,他会调用promise.resolve方法,将参数转换实例
还是以一个例子说明
const p = Promise.race([p1, p2, p3]);
p只有一种情况,只要p1,p2,p3任意一个的状态发生改变,p的状态也随之发生改变。
promise.any:
-
也是同时处理promise实例,包装成一个新的promise实例。
-
它的状态变化跟promise.race类似,只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成
rejected状态,包装实例就会变成rejected状态。 -
和
promise.race()不同点,就是promise.any()不会因为某个promise变成rejected状态而结束,必须要等到所有的参数promise变成rejected状态才结束
4. promise手写系列
参考链接:
- 新增属性篇
1. 请聊一下ES6新增的数据类型symbol?
其最大的特点是:唯一值,独一无二的。
应用场景如下:
- 作为对象属性名(key)
- 使用symbol来代替常量
2. 请聊一下Proxy和Reflect?
回答步骤:
- 什么是
proxy?
在目标对象之前架设一层拦截器,必须通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤修改。说上以下三点可以做的操作:
-
get(target,propKey,receiver):拦截对象属性读取 -
set(target,popKey,value,receiver):拦截对象属性的设置 -
has(target,propKey),拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值 -
deleteProperty(target,propKey)拦截delete proxy[propKey]的操作,返回布尔值
......
- 什么是
Reflect?
它是一种于proxy对象一样,也是ES6新增的api,它的设计目的如下几点:
- 将Object对象的一些明显属于语言内部的方法
- 修改某些object方法的返回结果,让其变得更合理
- 让object操作都变成操作成为函数行为
// 老写法
'assign' in Object // true
// 新写法
Reflect.has(Object, 'assign') // true
- reflect对象的方法与proxy对象一一对应,只要proxy对象,就能在proxy对象可以方便调用对应的reflect方法,完成默认行为,作为修改的基础,有了reflect对象更简单易懂
// 老写法
Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1
// 新写法
Reflect.apply(Math.floor, undefined, [1.75]) // 1
3. 请聊下Genertor函数
直接开讲:
- 在语法上,首先可以把它理解成,
Genertor函数是一个状态机,封装了多个内部状态 - 形式上,
Generator函数是一个普通函数,两个特征。一是,function关键字与函数名之间有一个信号;二是,函数体内部状态(yield语句在英语里就是“产出”)
应用场景:
- 异步操作的同步化表达(异步操作的后续操作可以放在
yield语句下面) - 控制流管理
- 部署
Iteratior接口 - 作为数据结构(可以看做一个数组结构,因为
Genertor函数可以返回一系列的值)
4. 谈一下Iterator遍历器和for...of...
主要聊以下几个方面:
- 什么是Iterator(遍历器)?
是一种机制,它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据只要部署iterator接口,就可以完成遍历操作。
- 遍历器的作用
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- ES6创造一种新的遍历命令
for...of循环,Iterator接口主要提供for..of消费
注意:Object.prototype上没有没有实现[Symbol.iterator ](),所以不能for...of遍历
- 调用iterator接口的场合
- 结构赋值
- 拓展运算符(
...) yield*[1,2,3,4]Promise.all(),Promise.race()Array.from()Map(),Set(),WeakMap(),WeakSet()
- 最后聊一下for...of
-
循环内部调用的是数据结构
Symbol.iterator方法 -
for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。
ps: 参考链接