最近面试问到的一些问题,一年经验问的都比较基础

119 阅读6分钟

CSS

1.bfc

一篇看懂bfc

2. 伪元素和伪类

伪元素(伪对象)不存在在DOM文档中,是虚拟的元素,代表某个元素的子元素,这个子元素虽然在逻辑上存在,但却并不实际存在于文档树中
主要有: :before、:after、::placeholder(设置对象文字占位符的样式)、::selection(设置对象被选中时的颜色)、:first-line(设置对象第一行的样式)、:first-letter(设置对象内第一个字符的样式)
伪类,存在DOM文档中,逻辑上存在但在文档树中却无须标识
伪类比较多,常见的有: :honver、:active、:focus、:link、:visited、:nth-child(n)、:nth-of-type(n)、:firstchild、:lastchild等等

3.flex:1

flex:1 === flex: 1 1 0%;
第一个参数表示: flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
第二个参数表示: flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
第三个参数表示: flex-basis 给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为auto, 即项目本身的大小

4.position

static: 正常布局行为,top、right、left、bottom、z-index等均无效(默认值)
relative: 元素会先放在未添加的定位的位置,在不改变页面布局的前提调整位置
absolute: 元素会脱离文档流,不再为元素预留空间,元素会相对最近的非static定位来确定元素位置,绝对定位的元素设置margin不会与其他的外边距合并
fixed:元素会脱离文档流,不再为元素预留空间,元素会相对屏幕来确定位置,滚动时元素不会改变位置
sticky: 元素根据正常文档流定位,会根据最近的'滚动祖先'来定位

JS

1.事件委托

又称事件代理,指把本来需要绑定在子元素的事件委托给父元素,通过父元素来监听事件,原理是事件冒泡。

<ul> 
<li>item 1</li> 
<li>item 2</li> 
<li>item 3</li> 
...... 
<li>item n</li> 
</ul>

举个栗子,给li添加点击事件,添加到ul上,减少了事件注册,大量节省内存

2.事件循环

这个讲起来比较复杂,我就简单说了一下
js引擎是单线程,分为同步和异步任务,同步任务在主线程上执行,异步任务则放在队列中,当主线程上的同步任务执行完毕,系统就开始读取队列中的异步任务,将它们推入执行栈中执行。
事件循环

3.js基础数据类型和数据检测方式

基础类型: String、Number、boolean、null、undefined
引用类型: object(function、Array、Date)

检测方法:

1.typeof
typeof '' // string
typeof 1 // number
typeof Symbol() // symbol
typeof true // boolean
typeof undefined // undefined
typeof null // object
typeof [] // object
typeof new Function() // function
typeof new Date() // object
typeof new RegExp() // object

对于基本类型,除了null都可以返回正确结果,对于引用类型,除了function其余均返回object
2.instanceof
instanceof用于检测A是否为B的实例,当A的__proto__指向B的prototype是,就可以认为A是B的实例

[] instanceof Array // true
{} instanceof Object // true
[] instanceof Object // true
new Date() instanceof Date // true
new Date() instanceof Object // true

function People(){}
new People() instanceof People // true
3.constructor
''.constructor===String // true
new Number(1).constructor===Number // true
true.constructor===Boolean // true
new Function().constructor===Function // true
4.toString()
Object.prototype.toString.call('') // [object String]
Object.prototype.toString.call(1) // [object Number]
Object.prototype.toString.call(true) // [object Boolean]
Object.prototype.toString.call(Symbol()) // [object Symbol]
Object.prototype.toString.call(undefined) // [object undefined]
Object.prototype.toString.call(null) // [object Null]
Object.prototype.toString.call([]) // [object Array]

4.防抖节流

防抖
在第一次触发事件时,不立即执行函数,而是给出一个期限值
如果在期限值内没有再次触发事件,那么就执行函数
如果在期限值内再次触发事件,那么当前的计时取消,重新开始计时
function debounce(fn,delay){
    let timer = null;
    return function() {
        if(timer) {
            clearTimeout(timer) 
        }
        timer=setTimeout(fn,delay)
    }
}
节流
对于短时间内连续触发的事件(例如滚动事件),防抖的含义就是让某个时间期限内,事件处理函数只执行一次。
function throttle(fn,delay) {
    let vaild=true;
    return function(){
        if(!vaild){
            return false
        }
        vaild = false
        setTimeout(()=>{
            fn()
            vaild = true
        },delay)
    }
}

5.回调函数和高阶函数

回调函数:不会立刻执行, 正如定义中给出的一样回调函数作为代码片段可以根据需要在其特定条件下执行, 回调函数作为参数传递给一个函数的时候,传递的只是函数的定义并不会立即执行
回调函数就是一个闭包的简单使用

高阶函数:函数可以接受另一个函数作为参数 function add(x,y,fn) { return fn(x)+fn(y) } add(5,6,Math.abs) ==> Math.abs(5)+Math.abs(6) ==> 11

6.迭代器

迭代器属于一种设计模式
JavaScript迭代器iterator

7.深拷贝、浅拷贝

基本数据类型存放在栈中,引用数据类型存在堆内存中在栈中引用
浅拷贝是复制了指针,新旧对象共享堆内的内存,深拷贝需要在改变新的对象(数组)时原对象(数组)的值不发生改变,需要在堆内开辟一个新的空间

深拷贝的实现:
1.JSON.parse(JSON.stringify(obj)),该方法只能处理数组和对象,不能处理函数

2.递归处理
function getType(obj){
    return Object.prototype.toString.call(obj).slice(8,-1)
} // 获取类型
function deepClone(target) {
    let result;
    const type = getType(target)
    if(type==='Object') {
        result = {}
    } else if (type==='Array'){
        result = []
    } else {
        return target
    }
    // 判断拷贝类型,初始化变量result
    
    for(i in target) {
        const value = target[i]
        if(getType(value)==='Object'||getType(value)==='Array'){
            result[i]=deepClone(result) // 嵌套数组或对象
        }else {
            result[i]=value
        }
    }
    return result;
}

3.loadsh库内提供_.cloneDeep()方法可以做深拷贝

8.async、await

async将常规函数转换成Promise,返回一个Promise对象,await放在async函数中,返回的是Promise成功的值,失败需要用try..catch捕获异常。如果多个await命令的操作返回值不存在依赖关系,使用Promise.all()让他们同时触发,执行速度更快。

9.promise同时请求多个数据

const p1 = new Promise((resolve,reject)=> {
    resolve()
});
const p2 = new Promise((resolve,reject)=> {
    resolve()
});
const p3 = new Promise((resolve,reject)=> {
    resolve()
});

const data = Promise.all([p1,p2,p3]) // 返回Promise对象
Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的

10.promise.all和promise.race的区别

Promise.all将多个Promise实例包装成一个Promise实例,成功返回一个结果数组,失败则返回首先被reject的值
Promise.race返回获取最快的一个结果,不管是成功或者失败

vue

1.vue路由缓存

缓存全部路由
<keep-alive>
    <router-view></router-view>
</keep-alive>

指定路由缓存
使用include
<keep-alive include="该路由的name名称">
    <router-view></router-view>
</keep-alive>

存在多个路由时,想缓存部分路由
 使用meta,在路由中添加以下属性
 meta: {keepAlive: true // 缓存}
 meta: {keepAlive:false // 不缓存 }
 然后在页面中
 <keep-alive >
     <router-view v-if="$route.meta.keepAlive"></router-view>
 </keep-alive>
 <router-view v-if="!$route.meta.keepAlive"></router-view>

2.vue生命周期

创建
beforeCreate
实例创建之前,vue实例挂载元素$el和数据对象<br/>data为undefined,未初始化
created
数据对象data已经存在,可以调用methods里面的方法,操作data里面的数据,但dom还未生成,$el不存在

挂载
beforeMount
vue的实例$el和data已初始化,挂载之前为虚拟dom,模板在内存中已经编辑完成尚未渲染到页面中,data中的数据还未替换
mounted
实例挂载完成,dom渲染完成

更新
beforeUpdate
当 data 变化时,会触发beforeUpdate方法 ,data 数据尚未和最新的数据保持同步
updated
当 data 变化时,会触发 updated 方法,页面和 data 数据已经保持同步了

销毁
beforeDestory
组件销毁之前调用 ,在这一步,实例仍然完全可用
destoryed
组件销毁之后调用,对 data 的改变不会再触发周期函数,vue 实例已解除事件监听和 dom绑定,但 dom 结构依然存在

react

1.react生命周期(新、旧)

挂载过程
constructor()
// 完成react数据初始化
componentWillMount()
// 组件已经初始化数据,但还未渲染dom执行的逻辑,主要用于服务端渲染
componentDidMount()
// 组件完成第一次渲染时的逻辑,此时dom节点已经生成

更新过程
componentWillReceiveProps(nextProps)
// 接受父组件新的props时,重新渲染组件执行的逻辑
shouldComponentUpdate(nextProps, nextState)
// setState后,state发生变化,组件进入重新渲染流程执行的逻辑,在此生命周期可以return false阻止组件更新,主要用于性能优化
componentWillUpdate(nextProps, nextState)
// shouldComponentUpdate返回true后,组件重新渲染执行的逻辑
render()
// 页面渲染执行的逻辑,render函数将jsx编译为函数并生成虚拟dom,然后通过diff算法比较前后的dom树,渲染改变后的节点
componentDidUpdate(nextProps, nextState)
// 重新渲染后执行的逻辑

卸载过程
componentWillUnmount()
// 组件卸载前执行的逻辑
react16.4更新了新的生命周期
getDerivedStateFromProps代替componentWillReceiveProps和componentWillMount
// 无论父组件的更新,props的变化或者执行了setState,都被调用
getSnapshotBeforeUpdate代替componentWillUpdate
// 在render之前调用,保证getSnapshotBeforeUpdate中读取的dom状态和componentDidUpdate中是一致的

2.react用过哪些hook

useState
// 函数组件中管理状态,唯一的参数是初始状态
useEffect
// 代替类组件的部分生命周期,默认每次渲染都会执行。第二个参数传空数组只会第一次渲染时执行,也可以绑定某个值,在这个值发生改变时再执行
useContext
// 跨组件共享数据,使用React.createContext()创建Context对象,使用<Context.Provider></Context.Provider>包裹子组件,数据通过<Context.Provider value={value}></Context.Provider>的方式传递,子组件通过useContext(Context)获取值
useCallback
// useCallback返回一个缓存函数,使用一组参数初次调用函数时,会缓存参数和计算结果,当再次使用相同的参数调用该函数时,会直接返回相应的缓存结果,第二个参数传一个依赖项,依赖值未发生变化时返回缓存的函数
useMemo
// 类似useCallback,区别是useMemo返回的缓存计算结果的值
useRef

3.函数组件模仿生命周期

useEffect(()=>{
    console.log('componentDidMount')
},[])

useEffect(()=>{
    console.log('componentDidUpdate')
},[n])

useEffect(()=>{
    return ()=>{
        console.log('componentWillUnmount')
    }
},[n])

4.react错误处理

错误边界,componentDidCatch

typescript

1.type和interface的区别

interface更侧重于数据结构,type侧重于类型,二者都可以描述对象或者函数,能相互extends,type专属功能:联合类型

2.讲一讲泛型

泛型可以用于类和构造器
class People<T> {
    private name: T
    constructor(val: T){
        this.name = val
    }
}
const p = new People<number>(12)

泛型可以用于函数
function people<T> (name: T): T {
    return name
}
people<string>('zhangsan')

算法

1.斐波那契数列

function fibonacci (n) {
    if(n<=2) {
        return 1
    } else {
        return fibonacci(n-1)+fibonacci(n-2)
    }
}

2.对象扁平化

function flatten(obj) {
    let result = {}
    function flatObj (key, value) {
        if(Object(value)!==value) {
            result[key]=value
        } else if (Array.isArray(value)) {
            value.forEach((it,index) => {
                flatObj(`${key}[${index}]`, it)
            })
            if (value.length === 0) {
                result[key] = []
            }
        } else {
            const objKey = Object.keys(value)
            objKey.forEach(it => {
                flatObj(key?`${key}.${it}`:`${it}`, value[it])
            })
            if(objKey.length === 0) {
                result[key]={}
            }
        }
        flatObj('', obj)
        return result
    }
}

3.链表反转