自己遇到的面试题

76 阅读5分钟

Webpack

  1. 常用的loader和plugin
loader:style-loader,css-loader,less-loader,babel-loader,ts-loader
plugin:html-webpack-plugin,clean-webpack-plugin
  1. abc.com与abc.com/app两者打包有什么不同

Vue篇

  1. keep-alive实现原理
  1. vue的插槽
vue大体分为三种插槽,匿名插槽、具名插槽、作用域插槽
匿名插槽:父组件想要向子组件传入一个模板进行渲染,可以使用slot
具名插槽:当想要传入多个时,就需要设置name

React篇:

  1. react的effect第一个参数为什么不可以是异步函数
  1. 受控组件与非受控组件
受控组件:不一定是表单元素,可以是任何组件,展示的value与React的状态绑定,页面数据变化时需要对应的change方法修改React的状态
非受控组件:像原声form表单元素一样,value保存在DOM内部,不与React的状态绑定
  1. react的性能优化
1.React.Memo缓存组件,当父组件重新render时,无论子组件是否发生变化,都会重新render一次,通过React.Memo缓存子组件,在父组件重新render时,对子组件的prop进行浅比较,如果没有变化,则直接使用缓存
  1. react和vue的区别
  1. 浏览器为什么不能解析jsx
  1. useState为什么返回的是一个数组?可以是对象吗?
为了解构方便,如果是对象的话解构需要写别名
  1. useEffect对应哪些钩子
  2. 父组件调用子组件的方法

ES6篇:

  1. 箭头函数为什么不可以作为构造函数:
new的简单实现:
function myNew(fn, ...args) {
    const obj = {};
    obj.__proto__ = fn.prototype;
    // ❌箭头函数没有prototype属性
    const res = fn.apply(obj, args)
    // ❌箭头函数没有自己的this,无法使用call、apply等方法改变this指向
    return typeof res === 'object' ? res : obj
}

因为箭头函数没有自己的this指向,也没有prototype和arguments属性,所以不能作为构造函数

  1. 解构函数是深拷贝还是浅拷贝
浅拷贝,const a = {...b}解构出来的如果有引用对象的话还是联动的,复制还是地址
Object.assign函数复制的也一样
  1. 手动实现深拷贝的递归时需要注意什么
const deepClone = (obj) => {
    if (typeof obj !== 'object' || obj === null) {
        return { ...obj }
    }
    const newObj = obj instanceof Array ? [] : {}
    for(const i in obj) {
        if(typeof obj[i] !== 'object' || obj[i] === null) {
            newObj[i] = obj[i]
        } else {
            newObj[i] = deepClone(obj[i])
        }
    }
    return newObj
}
  1. class的继承
// 父类
function Person(name){
    this.name = name
    this.eat=['banana']
    this.say = function(word){
        console.log(this.name + word)
    }
    sayHello = function() {
        console.log(this.name + ' Hello')
    }
}
Person.prototype.getName = function() {
    console.log(this.name)
}
// 原形链继承
/*  存在的问题,
*   1、子类的原型是父类的实例,当父类实例中有引用类型的对象时,子类实例会会共享这个引用对象,在其中一个子类中修改该对象,其他子类会被污染
*   2、实例化子类时无法向父类的构造函数传参数
*/
function Son(){}
Son.prototype = new Person();
const s1 = new Son()
const s2 = new Son()
s1.eat.push('apple')
console.log(s1 instanceof Son) // true
console.log(s1 instanceof Person) // true
console.log(s1.eat) // ['banana', 'apple']
console.log(s2.eat) // ['banana', 'apple']

// 构造函数继承
/** 在子类的构造函数中调用父类的构造函数,并通过call或apply指向子类,解决了原型链继承存在的污染问题
*   存在的问题:
*   1、子类无法访问到父类原型上的属性和方法
*/
function Son1() {
    Person.call(this)
}
const s11 = new Son1()
const s12 = new Son1()
s11.eat.push('apple')
console.log(s11 instanceof Son1) // true
console.log(s11 instanceof Person) // false
console.log(s11.eat) // ['banana', 'apple']
console.log(s12.eat) // ['banana', ]
s11.getName() // Uncaught TypeError: s11.getName is not a function

// 组合继承
// 把原型链继承和构造函数继承组合起来,两者的缺点都解决了,但是父类会被调用两次,有性能问题,
function Son2(name) {
    Person.call(this, name)
    this.name = name
} // 构造函数继承
Son2.prototype = new Person() // 原型链继承
const s21 = new Son2('bob')
const s22 = new Son2('pop')
s21.eat.push('apple')
console.log(s21 instanceof Son2) // true
console.log(s21 instanceof Person) // false
console.log(s21.eat) // ['banana', 'apple']
console.log(s22.eat) // ['banana', ]
s21.getName() // bob
s22.getName() // pop
console.dir(s21) 

// 寄生组合式继承
// 在组合继承的基础上,子类的prototype不再指向父类的实例,通过Object.create()方法拷贝一个父类prototype
function Son3(name) {
    Person.call(this, name)
    this.name = name
} // 构造函数继承
const extend = function(Parent, Child) {
    Child.prototype = Object.create(Parent.prototype)
    // 重写prototype属性会导致constructor丢失,手动加回去
    Child.prototype.constructor = Child
}
extend(Person, Son3)
const s31 = new Son3('bob')
const s32 = new Son3('pop')
s31.eat.push('apple')
console.log(s31 instanceof Son3) // true
console.log(s31 instanceof Person) // false
console.log(s31.eat) // ['banana', 'apple']
console.log(s32.eat) // ['banana', ]
s31.getName() // bob
s32.getName() // pop
console.dir(s31) 
  1. JSON.parse(JSON.stringfy())深拷贝的缺陷
/**
使用JSON.parse(JSON.stringfy())实现深拷贝时:
undefined、函数、Symbol类型的值会丢失
Date类型的值会转成字符串
NaN类型会转成null
*/
function Person (name) {
    this.name = 20
}

const lili = new Person('lili')

let a = {
    data0: '1',
    date1: [new Date('2020-03-01'), new Date('2020-03-05')],
    data2: new RegExp('\\w+'),
    data3: new Error('1'),
    data4: undefined,
    data5: function () {
    	console.log(1)
    },
    data6: NaN,
    data7: lili,
    data8: Symbol()
}

let b = JSON.parse(JSON.stringify(a))
/**
data0: "1"
data2: {}
data3: {}
data6: null
data7: {name: 20}
date1: (2) ['2020-03-01T00:00:00.000Z', '2020-03-05T00:00:00.000Z']
[[Prototype]]: Object
  1. for in 和for of的区别(还有Object.keys())
相同点: for infor of都可以用来遍历数组
不同点: for in遍历获得的是key,for of遍历获得的是value
        for in可以遍历对象,for of不行
var myObject={
  a:1,
  b:2,
  c:3
}
for(let i in myObject){ console.log(i) }
// a
// b
// c
for(let i of myObject){ console.log(i) }
// Uncaught TypeError: myObject is not iterable
for in还会遍历数组原型对象上的可枚举属性(Object.keys不会),例如:
Object.prototype.d = 4
for(let i in myObject) {
    console.log(i)
}
// a
// b
// c
// d
Object.keys(myObject) // [a,b,c]
除此之外,for in 的遍历顺序不是严格按照声明顺序来的,Object.keys()也有这个问题

CSS篇

  1. 清除浮动
  2. 两列布局实现

TypeScript

  1. 常用的工具函数
typeof:类型捕获
keyof: 键值捕获
const obj = {msg:'123'}
const func = (): typeof obj => {
    return obj
}

Partial<T>: 可以只返回T中的某一个属性(场景:传入某一个筛选条件)
Require<T>: 必须返回T中所有的属性
Pick<T, K>: 从T类型中选择K属性
Omit<T, K>: 从类型T中,剔除所有符合K条件的类型
Record<K, T>: 声明一个对象类型,其key类型为K,value类型为T
  1. 类型保护
  2. unknown和any的区别