【每日一练】前端面试题集锦2

基础知识

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
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值