面试题以单选,多选,简答和编程四部分为主。由于是需要手写的,所以后两个写下大致思路和伪代码就行。内容偏基础,但有一些都是犄角旮旯的知识点,而且单选和多选迷惑性很强。具体的原题已经记不得了,就以罗列知识点为主
一、原型和原型链
1. 第一个单选题涉及原型和原型链,有两个很不常见的选项,后面讲
2. 原型链三角,构造函数,实例和原型对象。构造函数通过new指向实例,实例的__proto__属性指向原型,构造函数的prototype属性指向原型
3. instanceof的原理就是查找前者的原型链上是否有该构造函数的原型
4. 其中一个选项是“函数的prototype属性指向原型”,我觉得是正确的,但是函数也太宽泛了,可以在浏览器控制台看到,箭头函数是没有prototype属性的,但是有__proto__属性
5. 有两个选项“所有对象都有__proto__属性”和“Object.create(null)创造的对象没有原型”。如果要在浏览器控制台查看的话,Object.create(null) 创建的对象没有任何属性,那这样前一个选项就是错的,不过依据MDN上的说法“使用 Object.create(),我们可以创建一个原型为 null 的对象。在字面量初始化对象语法中,相当于使用 __proto__ 键。”那我觉得后一个选项是错的。也不知道出题人怎么想的,希望以后这种模棱两可的东西滚出去。
6. 如果想在内置类型上添加方法,需要使用原型,例如 Number.prototype.add 这样
7. 判断类型方法 type of instanceof Object.prototype.toString.call
二、import和export
1. import *** from “” 和export default 配合使用,import {} from “” 和 export 配合使用
2. import * as name from "" 在MDN中称为命名空间导入,name就是一个对象,包含要export文件的所有内容,其中default属性表示默认导出内容
3. 模块内的变量是局部的,不会影响全局
4. import默认是静态编译的,也就是在编译过程就已经确认了导入的模块是啥,因此默认是同步的。import有引用提升置顶效果,也就是放在何处都会默认在最前面。但是**,** 通过import()动态引入是异步的哦,并且是在执行中加载的。 import()在真实业务中是很常见的,例如路由组件的懒加载component: () => import('@/components/dutest.vue')和动态组件const MyTest = await import('@/components/MyTest.vue');等等,import() 执行返回的是一个 Promise,所以经常会配合async/await一起用
5. import和export是ES6模块规范,在浏览器环境中使用,require和module.exports ,exports属于commonjs规范,在nodejs环境中使用。require是动态的,要在运行时确定导入内容。具体可以看developer.mozilla.org/zh-CN/docs/…
三、vue.nextTick
1. 官方解释nextTick是为了“等待下一次 DOM 更新刷新的工具方法”,因为vue是异步批量更新,所以如果要获取最新的DOM,可以使用nextTick,传入回调函数,返回一个Promise,可以在下次“tick”时执行回调。
2. 原理其实是使用Promise().resolve().then(fn) 创建微任务,fn就是传入的回调函数。如果Promise不支持可以用**MutationObserver,**监听DOM变化,第三种方法是setTimeout
3. 照这样看nextTick的本质是往微任务队列里插入一个任务,在window中也有一个api可以插入微任务window.queueMicrotask()
4. watch中也可以设置在DOM更新后执行副作用,他的执行时机会比nextTick早
四、useEffect的依赖项
1. useEffect的依赖项有三种情况,一是不写依赖项数组,那会在初次和每次更新时都执行;二是空数组,那只会在组件初次渲染时执行;三是填入数据,则会在数据发生变化时执行
2. 依赖项的变化是基于Object.is来判断的,是浅层的比较,即两个对象地址不一样,就认为发生变化
3. 依赖项也可以填入对象和函数,但在某些情况下这种没有意义,因为函数式组件更新时会重建他内部的变量,这样监听的对象和函数每次都认为是更新,副作用函数每次都执行
4. useEffect副作用函数的返回值是清除函数,会在下次更新前用旧的props和state执行清除函数
五、css层级上下文
1. 虽然屏幕是二维的,但在html和css中有z轴的概念,各个元素在z轴上层叠排布,这也是
z-index实现的意义
2. 文档根元素,设置transform和设置position:fixed 等都会创建层叠上下文,需要注意的是opacity(透明度)小于1的元素也会创建层叠上下文,具体可看MDN
3. 有时候设置z-index不生效,就需要看你要作用的两个元素是否在同一个层叠上下文中,或者z-index设置的数字超出范围了
4. 更多详细内容可看 juejin.cn/post/727304…
六、vue的Teleport
1. 是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去
2. 主要是为了解决组件逻辑和html结构分离,可以利用上面的to属性,将一个组件渲染到另一个元素内
3. 具体详情可看官网 cn.vuejs.org/guide/built…
七、深拷贝
1. 最简单的深拷贝JSON.parse(JSON.stringify({}))
2. 其实原生已经支持深拷贝 window.structuredClone(),详见MDN developer.mozilla.org/zh-CN/docs/…
3. 一般面试写深拷贝就记住三点,一是递归,二是判断数组和对象,数组用map,对象用for.in,三是其他普通值直接返回,示例
const clone = (target) => {
if (target instanceof Array) {
return target.map(item => clone(item))
}
if (target instanceof Object) {
const obj = {}
for (let key in target) {
obj[key] = clone(target[key])
}
return obj
}
return target
}
八、css权重
1. id选择器 > 类选择器 | 属性选择器 | 伪类 > 标签 | 伪元素
2. !important 是例外,优先级很高,可以覆盖内联样式
3. 更多详情可看MDN developer.mozilla.org/zh-CN/docs/…
九、盒模型
1. 从外到内依次是margin border padding width
2. 标准盒模型是box-sizing: content-box 即width等于最里层的内容宽度,box-sizing: boder-box 表示width等于border+padding+content
十、reduce,eval,Math和parseInt
1. 给出四个选项,问哪个可以打印出6
A: [1,2,3].reduce((a,b)=>a+b)
B: eval('3+3')
C:Math.max(...[1,2,3])
D: parseInt('10',2)
2. reduce是从左到右计算数组里的每一项,它的第一个参数是计算函数,第二个是默认值,如果没有默认值的话,就把数组第一项当作默认值并且跳过第一次循环
3. eval是将字符串当作JS代码来执行
4. Math.max是取出数字中的最大值
5. parseInt是将字符串转为十进制数字或者NaN,第二个参数表示被转换字符串的进制,是2到36,如果不在这个范围内,或者字符串的第一个非空字符不能被转成数字,那就返回NaN。
6. 如果你不传,那他就依据字符串自行判断当前进制,通常是十进制,但某些情况下也会是八进制和十六进制。如果传入的字符串不符合进制,那会进行截取或者返回NaN
十一、slice和splice
1. slice会截取数组,返回截取的部分。两个参数,第一个是start,第二个是end,截取时不包括end。end会转为整数。不写end默认截取到数组末尾。除此之外,可以把start和end看作是数轴上的两个点,只有这个点在start右侧时,截取才有结果,小于等于都会返回空数组。注意这里描述的位置是说和数组长度比的相对位置,不是start和end的大小。end为负数相当于倒着取数组的某个位置。详见developer.mozilla.org/zh-CN/docs/…
2. splice是删除或添加元素,会改变原数组。参数为start,要删除的个数,以及要添加的元素。如果不传任何参数,则相当于不删除,大于数组长度相当于从数组末尾开始,除此之外都以0开始。删除的个数如果是undefined,负数和0,则不删除,此外如果不传或者超出数组长度则删除至数组末尾。
3. 剩余的参数就是往数组内插入的元素,表现行为是在start处向前插入
4. JS新提供了toSpliced方法,与splice类似,但不会改变原数组
十二、可枚举属性
1. Object.keys仅会迭代自身可枚举属性
2. for..in..会迭代自身和原型链上的可枚举属性
3. Object.getOwnPropertyNames() 会返回自身所有可枚举和不可枚举属性
4. Object.hasOwn(obj,name) 和obj.hasOwnProperty(name) 可以用来判断对象上某一个属性是否是自身的而不是继承的
5. 设置一个对象的某个属性的描述,可以使用Object.defineProperty() , 详见developer.mozilla.org/zh-CN/docs/…
十三、null == undefined
1. null == undefined 返回true
2. null == '' null == false null == 0 null == [] null == NaN 都是false
3. undefined == '' undefined == 0 undefined == [] undefined == false undefined == NaN 都是false
4. 1+1 =2 1+null = 1 1+ true = 2 1+ false = 1 这些都是数字 boolean和null 会被转成数字
5. 1+'' = '1' 1+[] = '1' []+'' = '' 出现字符串的话,加法就变成字符串拼接了
6. NaN与数字相加返回NaN,包括boolean和null这两个可以被转成数字的,与字符串就是‘NaN’
7. Falsy假值包括 ‘’ 0 false null 和undefined ,其他的包括 [] 和 {} 都是Turthy 真值
8. 数组转成字符串,会被拍平解构,用 ‘,’ 进行字符串拼接,如果有需要再转成数字,出错就是NaN