# es6+

# 1.装饰器了解过吗?

装饰器是个函数,可以给类加东西。@+函数名

function testFc(target) {
    target.name = "123" // 添加属性name
    console.log('调用了')
}

@testFc
class MyTestClass {

}
1
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

# 手写任意一种继承?

原型链继承=>弊端是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

# 列表继承类型及优缺点?

  • 原型链继承 缺点:原型对象的属性和方法被所有实例共享 构造函数实例话对象无法进行参数传递
  • 构造函数继承 缺点:解决了原型链继承的问题,但不能继承构造函数原型,只能继承构造函数的属性和方法。(常用原型链+构造函数,称为组合继承)
  • 组合式继承 缺点:无论什么情况下,都会调用两次父级构造函数:一次是在创建子级原型的时候,另一次是在子级构造函数内部
  • 寄生继承 缺点:无法复用
  • 原型式继承 缺点:无法复用
  • 寄生组合式继承 目前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

ES6:通过proxy代理,即使属性不在对象中,也可以监听

var user = new Proxy({},{
    set:function(target,key,value,receiver){}
})
1
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

# 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

# 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