目录
基础知识
1. (vue2) data里的变量如何变成不是响应式
- i: 将数据不在data中定义,而在created之后的钩子函数中动态定义数据变量。
- ii:在组件实例外定义数据
- iii:使用
Object.freeze
冻结数据
<template>
<div class="home">
<HelloWorld :msg="msg.key"/>
<button @click="changeMsg">改变msg</button>
</div>
</template>
<script>
... // 无关代码
data() {
return {
count: 1,
msg: Object.freeze({key: 'HelloWorld'})
}
},
methods: {
changeMsg() {
this.msg.key='测试数据响应式变化' + this.count++
console.log(this.msg.key)
}
}
... // 无关代码
</script>
2. (vue2) 父组件怎么知道子组件渲染完成
Vue的子组件又两种引入方式:同步引入,异步引入。不同的引入方式父子组件的加载顺序是不一样的
-
同步引入
- 引入方式:
import HelloWorld from '@/components/HelloWorld.vue'
- 父子组件初次进入 的生命周期顺序:父(beforCreate) -> 父(created) ->父(beforeMount) -> 子(beforeCreate) -> 子(created) -> 子(beforeMount) -> 子(mounted) -> 父(mounted)
- 父组件触发子组件更新时 的生命周期顺序:父(beforeUpdate) -> 子(beforeUpdate) -> 子(updated) -> 父(updated)
- 如何知道子组件渲染完成:
- i. 父组件mounted中子组件已经渲染完成
- ii. 可以在子组件中自定义事件,子组件挂载完成后触发该自定义事件
- iii. 可以使用
$nextTick
- 引入方式:
-
异步引入
- 引入方式:
const HelloWorld = () => import('@/components/HelloWorld.vue')
- 父子组件初次进入的生命周期顺序:父(beforeCreate) -> 父(created) -> 父(beforeMount) -> 父(mounted) -> 父(beforeUpdate) -> 子(beforeCreate) -> 子(created) -> 子(beforeMount) -> 子(Mounted) -> 父(updated)
- 父组件触发子组件更新时的生命周期顺序:父(beforeUpdate) -> 子(beforeUpdate) -> 子(updated) -> 父(updated)
- 如何知道子组件渲染完成:可以在子组件中自定义事件,子组件挂载完成后触发该自定义事件
- 引入方式:
<!-- 父组件 -->
<HelloWorld :msg="msg.key" @rendered="onRendered"/>
<!-- 子组件 -->
...
mounted () {
console.log('children mounted')
this.$emit('rendered')
},
...
3. (js) 深拷贝和浅拷贝有什么区别,JS怎么实现深拷贝
- i. 浅拷贝是指复制对象时,只复制对象的引用,而不是对象本身。浅拷贝会创建一个新对象保持对原对象中的引用类型(如数组、对象)引用关系,而不复制引用类型的内容。因此修改浅拷贝对象中的引用类型时,也会影响到原对象。
- ii. 深拷贝是指复制对象时,不仅复制对象的基本类型,还递归复制对象内部的所有引用类型,创建出一个独立的新对象。深拷贝后的新对象与原对象之间不会共享内存,即修改新对象中的引用类型,原对象不受影响。
- iii. 实现深拷贝:
- 使用
JSON.parse(JSON.stringify())
实现深拷贝。该方法不能处理函数,undefined
,循环引用等特殊情况 - 递归拷贝:手动遍历对象的属性,递归地进行深拷贝。可以处理复杂情况,但实现较麻烦
- 使用
deepClone(obj) {
if(obj === null || typeof obj !== 'object') {
return obj; // 处理基础类型
}
if(obj instanceof Date) {
return new Date(obj); // 处理Date对象
}
if(obj instanceof Array) {
return obj.map(item => this.deepClone(item)); // 处理Array对象
}
if(obj instanceof Object) {
const cloneObj = {}
for (let key in obj) {
if(obj.hasOwnProperty(key)) {
cloneObj[key] = this.deepClone(obj[key]) // 递归拷贝
}
}
return cloneObj
}
}
- 使用第三方库:如
lodash
提供的_.cloneDeep
方法 - iv: (补充)实现浅拷贝的有多种:
- 直接复制:
const copyObj = this.originObj
- 使用
Object.assign()
,例如:const copyObj = Object.assign( {}, this.originObj)
- 使用扩展运算符:
const copyObj = {...originObj}
- 直接复制:
4. (js) typeof和instanceof有什么区别?
typeof
- i. 功能:用于检测变量或表达式的数据类型
- ii. 返回值:String
console.log(typeof undefined) // undefined
console.log(typeof true) // boolean
console.log(typeof 1) // number
console.log(typeof 'hi') // string
console.log(typeof {}) // object
console.log(typeof []) // object
console.log(typeof null) // object, null的二进制表示和对象的类型标识符相同,0
console.log(typeof NaN) // number
console.log(typeof function () {}) // function
console.log(typeof Symbol()) // symbol, 符号(ES6)
console.log(typeof new Date()) // object
console.log(typeof /abc/) // object
console.log(typeof new Error()) // object
console.log(typeof 10n) // bigint, 大整数(ES11)
instanceof
- i. 用于检测某个对象是否是另一个对象(构造函数的实例)
- ii. 返回值: boolean
- iii. 使用场景:检测复杂类型(对象、函数、数组等); 检测某个对象是否继承自某个构造函数的原型。
function Person (name) {
this.name = name
}
const alice = new Person('Alice')
console.log(alice instanceof Person) // true
console.log(alice instanceof Object) // true
console.log(Object.getPrototypeOf(alice) === Person.prototype) // true
const arr = [1, 2]
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true
console.log(arr instanceof Person) // false
5. (css) sprites是做什么的
将多个小图标或者图像合并到一张大图中的技术,通过CSS的background-position属性来显示大图中的特定部分,从而实现不同需要显示不同的图像。
- 优势:减少HTTP请求次数,提升页面加载速度;同意管理图标和图像
- 缺点:维护复杂——当需要修改增删改某个图标,需要重新生成整个Sprite图像,并更新响应的background-position值
6. (css) box-sizing属性
默认值:content-box (标准盒模型) 。 border-box(IE 盒模型) 。
使用范围:所有可以设置宽width和高height的元素。
关于盒模型相关的图解和宽高计算,请参考上一篇 【每日一练】面试题集锦1
数据结构与算法
题目:力扣430. 扁平化多级双向链表
难度:中等
标签:深度优先搜索、链表、双向链表
参考题解(TypeScript) ----执行用时64ms, 击败100%
/**
* Definition for _Node.
* class _Node {
* val: number
* prev: _Node | null
* next: _Node | null
* child: _Node | null
*
* constructor(val?: number, prev? : _Node, next? : _Node, child? : _Node) {
* this.val = (val===undefined ? 0 : val);
* this.prev = (prev===undefined ? null : prev);
* this.next = (next===undefined ? null : next);
* this.child = (child===undefined ? null : child);
* }
* }
*/
// 测试数据
// case1: [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]
// case2: [1,2,null,3]
// case3: []
function flatten(head: _Node | null): _Node | null {
let cur = head
while (cur) {
if (cur.child) {
// 如果有子链表,先处理子链表,并插入node与node的下一个节点之间
const next = cur.next, // 先保存原有的下一个node
cHead = flatten(cur.child); // 递归将子链表也扁平化
cur.next = cHead; // 将当前node的下一个设为子链表的head
cur.child = null; // 将子链表置为null
cHead.prev = cur; // 将子链表的prev指向当前node,实现插入
while (cur.next) cur = cur.next // 遍历查找子链表的最后一个
if (next) {
cur.next = next; // 找到子链表的最后一个,将其next设为父node的下一个node
next.prev = cur
}
}
cur = cur.next
}
return head
};