为期一个月的面试题纪实(持续更新...)

113 阅读7分钟

HTML+CSS

juejin.cn/post/717038…

Javascript

juejin.cn/post/717031…

1.什么是事件委托?
  • 概念:将子元素的事件委托给父级元素去处理
  • 好处:节约内存+方便管理+防止内存泄漏
  • 节约内存:事件监听器的数量少
  • 方便管理:当子元素动态删减时,无需考虑其事件监听器动态删减的问题
  • 防止内存泄漏:当子元素删除时,无需删除其对应的事件监听器
  • ps:何为内存泄漏?
2.何为内存泄漏?
  • 用闭未释放的【全局变量+闭包+定时器+事件监听器】
  • 当我们删除DOM元素时,忘记删除其对应的事件监听器
  • 当我们离开页面时,没有释放掉各种的事件监听器
  • 用完的全局变量没有释放,obj = null
  • 用完的定时器没有clear掉
  • 用完未释放的数据闭包,newFn = null
3.ES6有哪些新特性?
  • 分为四个层面:变量层面+数据结构层面+函数层面+类层面
变量层面:
  • let:克服了var的弊病(变量提升+作用域穿透+重复声明)
  • const:把不希望被改变地址的对象声明为常量
数据结构层面:
  • 数组的展开+解构
  • 对象的简写+解构
  • 字符串:模板字符串
  • Map:方便增删改查的键值对存储器
  • Set:有序且不重复的元素集
函数层面:
  • 箭头函数:this从父级作用域当中继承
  • ps:this有哪些存在的情形?
类层面:
  • class系列语法:class(构造函数+原型属性),constructor(函数构造器),static:静态成员(属性+方法),super()调用父类的属性+方法,super(name)调用父类构造器,super.xxx() 调用父类实例方法
4.this所有可能情形?
  • 普通函数:函数的this为该函数的调用者(显式主语/无显式主语时,隐式的为window/DOM元素的事件监听器的this恒为事件源e.currentTarget)
  • 箭头函数:函数的this从父级作用域当中继承(可以认为箭头函数就是没有this)
  • 普通函数可以通过call(),apply(),bind(),来改变函数的this
  • constructor构造器当中的this,为当前构造的实例
  • 实例方法当中的this,为当前实例
  • 静态属性/方法当中的this,为当前类
5.请说出十个请求头
6.请说出十个响应头
7.什么是深拷贝以及什么是浅拷贝
8.什么是值传递以及什么是引用传递
9.谈谈对事件传播/派发机制的理解
10.谈谈对闭包的理解

Git

Vue

juejin.cn/post/717031…

1.谈谈你对Vue的理解
  • MVVM:VM层相当于MVC分层当中C层的一部分特定职能的分离,即实时监控数据和读取数据变化,当数据修改时,及时通知视图更新。
  • 响应式:与VM本质相同,提法不同,都是数据驱动视图,声明响应式数据,当数据变化时,驱动视图更新。
  • 组件化开发:把一个页面分隔成若干个小块(组件),每个组件都有自己的模板,数据,样式,逻辑,相互独立,互不干扰,目的就是为了方便复用。
  • 虚拟DOM:把真实的DOM结构映射成JS对象,目的就是为了,当数据发生变化时,能够快速的递归比较出发生变化的节点,比较的过程就是diff算法的过程。
  • diff算法:对数据实行差量渲染,最大化的使用上一次缓存的结果,前提是需要得到两个新老虚拟DOM的不同之处。在暴力递归的基础做了很多优化,例如用key去唯一标识特定的节点。
  • SPA单页面应用开发框架:当浏览器的路由地址发生变化时,是在同一个页面里面切换不同的组件,实现了整站开发的效果。
2.列表当中key的作用
  • 在diff算法的过程当中,提升了很大的性能
  • 当新老虚拟DOM的key相同时,直接认定其节点的整个DOM结构没有发生变化,直接使用上一次老的虚拟DOM渲染的结果,不带对其进行深入的递归比较,极大的提高了性能。
  • 当新老虚拟DOM的key不同时,也不带对其节点进行深入的递归比较,而是直接把老的干掉,原地根据新的数据进行渲染新的节点。
3.computed和watch的区别
  • 共同点:都是侦听一手数据状态变化,从而触发相应的逻辑
  • computed:专注于根据一手数据状态变化换算出二手数据状态,即计算属性
  • computed只有在计算属性依赖的数据项发生变化时,才会重新根据新的一手数据状态进行计算,否则直接复用上一次的渲染结果。
  • computed不应该产生副作用。
  • watch是专门侦听特定数据的变化,从而触发相应的副作用逻辑。
4.Vue的生命周期有哪些
  • 创建实例阶段(init option):beforeCreate,created
  • 挂载阶段(渲染DOM):beforeMount,mounted
  • 数据更新阶段(数据驱动视图):beforeUpdate,updated
  • 卸载阶段(渲染条件不成立/用户切换页面):beforeUnmount,unmounted
  • Vue2当中两个卸载阶段:beforeDestroy,destroyed
  • 八大生命周期图解: image.png
5.父子组件生命周期联动
  • 原则:父组件搭台,子组件唱戏
  • 创建实例与挂载阶段:父组件创建实例完毕并预备挂载,子组件一一创建实例完毕并挂载完毕,父组件宣布整体挂载完毕。
  • 数据更新阶段:父组件预备更新,子组件一一更新完毕,父组件宣布整体更新完毕。
  • 卸载阶段:父组件预备卸载,子组件一一卸载完毕,父组件宣布整体卸载完毕。
6.Vue有哪些常用的修饰符
  • 事件修饰符:stop、prevent、capture、once、self
  • 键盘修饰符:esc,ctrl,shift,meta,enter
  • 系统修饰符:exact
  • v-model修饰符:lazy,number,trim
  • 自定义v-model修饰符
  • 自定义指令修饰符
7.v-if和v-show的区别
  • 渲染条件不成立时:v-if压根就不存在,v-show不显示(display:none)
  • 适合使用v-show的场景:频繁的切换显隐
  • 适合使用v-if的场景:一锤子买卖(例如某某电影专区,需要VIP才可以观看)
8.谈谈你对nexttick的理解
  • 当数据变化时,无法立即拿到最新状态下的DOM
  • 数据变化是立刻完成,渲染跟进是需要时间的(异步)
  • 可以通过nexttick(()=>{在这里可以拿到最新的DOM状态})
  • 在nexttick回调函数当中,是在DOM结构渲染(更新)完毕才回调,这时候就可以拿到DOM的最新状态
9.什么是具名插槽以及什么是作用域插槽?
  • 具名插槽:就是插槽内容有很多,需要指定某些内容到哪个插槽当中,就需要指名道姓
//子组件
<son>
    <slot name="head">
        <P>我是head具名插槽的默认内容</p>
    </slot>
</son>

//父组件
<father>
    <template #head>
        <P>我是外界注入的head插槽的内容</p>
    </template>
</father>
  • 作用域插槽:父组件在自己的模板上部署子组件及其插槽内容时,只能使用父组件自身作用域内的数据,要想子组件内部的数据,就需要用到作用域插槽
  • 作用域插槽,子组件需要自己暴露数据供父组件使用,暴露的数据,会以一个对象的方式,父组件只需起一个别名接收即可
//子组件
<son>
    <slot name="head" :age="18" :color="red">
        <P>我是head具名插槽的默认内容</p>
    </slot>
</son>

//父组件
<father>
    <template #head="newObj">
        <p>{{newObj.age}}</p>
        <p>{{newObj.color}}</p>
        <P>我是外界注入的head插槽的内容</p>
    </template>
</father>
10.Vue组件通信方式有哪些?
  • 父子通信:prop down
  • 子父通信:event up
  • 祖孙通信:provide + inject

provide() 接受两个参数:第一个参数是要注入的 key,可以是一个字符串或者一个 symbol,第二个参数是要注入的值。

<script setup> 
import { ref, provide } from 'vue'

// 提供静态值
provide('foo', 'bar')
// 提供响应式的值
const count = ref(0) provide('count', count)

</script>
<script setup>
import { inject } from 'vue'

// 注入值的默认方式
const foo = inject('foo')
// 注入响应式的值
const count = inject('count')
// 注入一个值,若为空则使用提供的默认值
const bar = inject('foo', 'default value')
// 注入一个值,若为空则使用提供的工厂函数
const baz = inject('foo', () => new Map())
// 注入时为了表明提供的默认值是个函数,需要传入第三个参数
const fn = inject('function', () => {}, false)

</script>
  • 子组件自己暴露给父组件:expose
//子组件
export default {
    expose:{
        myname,
        myage,
        sayHelloFn
    }
}

//父组件
<son ref="sonRef"/>
this.$refs.sonRef.sayHelloFn()
//Vue3
//子组件
<script setup>
import { ref,defineExpose } from 'vue'

const a = 1
const b = ref(2)

defineExpose({
  a,
  b
})
</script>

//父组件
<son ref="sonRef"/>
this.$refs.sonRef.a
  • 使用全局状态管理自由通信:把需要共享的数据状态都存储在【具有响应式的中央数据仓库里】使用vuex/pinia,并且对外暴露CRUD接口,任意组件去修改数据状态,其他用到改状态的组件,都会具有响应,根本不必去可以通信