# es6+
# 1.装饰器了解过吗?
装饰器是个函数,可以给类加东西。@+函数名
function testFc(target) {
target.name = "123" // 添加属性name
console.log('调用了')
}
@testFc
class MyTestClass {
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 2.var和const,let区别?
- var声明的变量是全局的,而let,const声明的变量是块级的。
- var声明的变量存在变量提升, let,const不存在。 (变量提升:可以在声明变量前使用这个变量,但是拿到的是undefined,拿不到所赋的值)
# 变量提升的原因?
js代码在运行前都会进行AST(抽象语法树)解析,函数声明和变量声明都会提升到最前面,但赋值不会提升
# 3.箭头函数和普通函数区别?
- 箭头函数的this继承外层普通函数的this,如果没有普通函数,this指向window(node中指向空对象)
- 箭头函数没有构造器,没有原型属性(prototype),没有arguments对象
# vue生命周期的钩子函数为什么不能用箭头函数?
箭头函数的this关键字不会绑定到vue实例,会报错
# *4.es5和es6继承有什么不同?
- es5的继承是通过构造函数或原型来实现,先创建子类的实例对象this,再将父类的方法绑定到this上
- es6是先创建父类的实例对象this,再用子类的构造函数修改this。 (子类的构造函数中,只有调用super后才能使用this,因为子类没有自己的this,而是继承了父类的this) (super调用父类的构造函数)
//ES5继承 寄生组合继承
function Parent(name) {
this.name = name;
}
Parent.prototype.eat = function() {
console.log(this.name + ' eating')
}
function Child(name, age) {
Parent.call(this,name)
this.age = age
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
let aa = new Child('aa', 123)
console.log(aa.name)
console.log(aa.age)
aa.eat()
//ES6继承
class Parent {
constructor(name) {
this.name = name
}
eat() {
console.log(this.name, 'eating')
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
}
let aa = new Child('aa', 123)
console.log(aa.name)
console.log(aa.age)
aa.eat()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 手写任意一种继承?
原型链继承=>弊端是cat.prototype.constructor指向animal,需要手动改成cat,cat实例化只能继承animal的方法,不能继承属性
function animal() {
this.name = 'dog'
}
function cat() {}
cat.prototype = new animal()
构造函数继承=>弊端是cat只能继承animal的属性,不能继承方法
function animal() {
this.name = 'dog'
}
function cat() {
animal.call(this)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 列表继承类型及优缺点?
- 原型链继承 缺点:原型对象的属性和方法被所有实例共享 构造函数实例话对象无法进行参数传递
- 构造函数继承 缺点:解决了原型链继承的问题,但不能继承构造函数原型,只能继承构造函数的属性和方法。(常用原型链+构造函数,称为组合继承)
- 组合式继承 缺点:无论什么情况下,都会调用两次父级构造函数:一次是在创建子级原型的时候,另一次是在子级构造函数内部
- 寄生继承 缺点:无法复用
- 原型式继承 缺点:无法复用
- 寄生组合式继承 目前js继承都是用这个方法。结合了组合继承和寄生继承的优点。
# 5.ES6新特性?
- async,await
- 箭头函数 function(参数) {} (参数)=> {}
- const,let
- 解构赋值=>对象
- 扩展运算符
- 模板字符串
- for of遍历(for in不是es6新增)
- set/weakSet map/weakMap
- class类, 装饰器
- generator
- reflect/proxy
- promise
- symbol/bigInt=》构造函数返回一个symbol,保证是唯一的。不能new=》即使传入相同的值生成的值也是唯一
- 可选链?.
- Object.entries()=>Object.fromEntries()=>对象
- Array.at() =>获取某个索引的元素 arr = [0, 1, 2] arr.at(2) =>就是2
- Array.from()将类数组和可遍历(iterable)对象转数组 =>数组
- find()=>数组
- includes()=>数组
- flat(),flatMap()=>数组
- Object.assign()=>对象
# proxy和reflect=》改变this指向
- proxy代理对象,可以拦截对象的读和写操作
- let proxy = new Proxy(target, handler) target就是要代理的对象 handler定义了代理后的操作 =》比如代理后的读写操作get,set就是在handler里
- reflect反射,(不是构造函数不能通过new调用),提供拦截js操作的方法=》比如reflect.defineProperty
- 作用修改某些object的返回结果,比如Object.defineProperty无法定义时会抛出错误,而reflect只会返回false
- 与proxy相对应,proxy对象的方法,在reflect对象上都能找到
- reflect所有属性和方法都是静态的
# 常用api:
- reflect.apply等同apply=》改变this指向
- reflect.defineProperty等同Object.defineProperty
# ES7
- Array.prototype.includes()
- **幂运算符
# ES8
- async await
- Object.values()
- Object.entries()
# ES9
- for await ... of
- 对象扩展操作符{...obj}
- promise.prototype.finally()
# ES10
- Array,prototype.flat()/flatMap()
- Object.fromEntries()
- String.prototype.matchAll()
- BigInt
# ES11
- promise.allSettled
- 可选链?.
- 空值合并运算符??=》左侧为null或undefined,返回右侧,否则返回左侧
# ES12=》2021.02.20
- String.prototype.replaceAll() =>a.replaceAll('2', '3') 直接替换,不用通过之前/g全局更新的方式
- Promise.any =>和promise.race类似,但它会等到所有promise都失败之后,才返回失败的值。.race一个成功就返回第一个promise的值作为返回值
- WeakRef
# ES13=》2022 在今年6月已经成为标准
Array.at()
语法糖:糖衣语法,语法糖能够增强程序的可读性,减少代码出错的机会,把复杂的方法进行封装。 forEach就属于语法糖
# 6.es5和es6监听对象属性改变区别?
ES5:通过defineProperty,如果属性不在对象中,则不能监听
Object.defineProperty(user,'name',{
set:function(key,value){}
})
1
2
3
2
3
ES6:通过proxy代理,即使属性不在对象中,也可以监听
var user = new Proxy({},{
set:function(target,key,value,receiver){}
})
1
2
3
2
3
# 7.async,await实现了generator,如何实现?
function* myGenerator() {
console.log(yield Promise.resolve(1)) //1
console.log(yield Promise.resolve(2)) //2
}
function run(myGenerator) {
var g = gen(); //由于每次gen()获取到的都是最新的迭代器,因此获取迭代器操作要放在step()之前,否则会进入死循环
function step(val) { //封装一个方法, 递归执行next()
var res = g.next(val) //获取迭代器对象,并返回resolve的值
if (res.done) return res.value; //递归终止条件
res.value.then(val => { //Promise的then方法是实现自动迭代的前提
step(val) //等待Promise完成就自动执行下一个next,并传入resolve的值
})
}
step(); //第一次执行
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# generator?
.next()返回一个对象{value: '',done: true}=>当done为true表示都执行完毕 第一个yield拿到的是第二个next()传递的参数
function *test() {
let input1 = yield "1111"
console.log(input1)
}
let tt = test()
let a = tt.next('333')
console.log(a)
let b = tt.next('444')
console.log(b)
// 结果
{ value: '1111', done: false } // 第一次next到yield输出就结束了 拿不到传递的333
444
{ value: undefined, done: true } // 第二次next才能被接收到输出444
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 8.forEach,for of区别?
- forEach里面不能执行异步函数,for of可以
- forEach不支持break,continue,for of可以
# for...of 和 for...in区别?
- for of常用来遍历数组,for in遍历对象(for in 遍历会到原型上)
- for of不能直接遍历对象
# 9.async和await缺点?
await会阻塞代码,滥用可能会导致代码失去并发性
# 10.set和map有什么区别?
- set类似于数组,但是所有值都是唯一的
- map类似于对象,也是键值对的集合。对象要求键是字符串,map允许任意类型当成是键
# set为什么能去重?
- 会先调用对象的hash方法,获取hash结果
- 如果结果相同,调用eq()方法去判断二者的值是否相等,eq本质就是==,都相等就去重
# 11.map和weakMap区别?
- map可使用任何数据类型作为key,weakMap只能用引用类型作为key。
- map不会被垃圾回收,weakMap会被垃圾回收。因为weakMap是弱引用,不在内部维护两个数组,所以也无法遍历(没有keys(),values(),entries(),size,clear()) (weakMap只有get(),set(),has(),delete())
# set和weakSet区别?
weakSet是弱引用,无法遍历。并且只能存对象引用,不能存放值,set都可以
# 12.??作用
- 和||(或)类似,只有变量为null或undefined才使用后面的值
- 而||只要前面是false就取后面
# 区别
- 0||1结果1 0??1结果0
- false||123结果123 false??123结果false
# 13.什么是可选链?如何访问数组?
- ?.操作符,可以嵌套获取对象的属性值。且对象的属性值可能是undefined或null。a?.b?.c
- obj?.a?.[0]即可访问数据,通过数组下标
# 14.异步的几种解决方案?
- 回调函数
- promise
- generator
- async/await