当前位置: 首页 > news >正文

2024前端面试题(持续更新)

目录

一、js的数据类型有哪些?

二、什么是symbol?

三、什么是浅拷贝什么是深拷贝?

四、vue2的生命周期?

五、vue2中父子组件的生命周期调用顺序

六、vue3的生命周期

七、vue3对比vue2的变化

八、组合式API中的ref和reactive是什么?

九、什么是闭包?

十、什么是垃圾回收机制

十一、computed和watch的区别?

十二、组件间通信的方式?

十三、什么是vuex和pinia?

十四、v-if和v-show的区别?

十五、v-if 和 v-for 为什么不建议放在一起使用

十六、为什么data是一个函数而不是一个对象?


一、js的数据类型有哪些?

1、基本数据类型

string字符串、number数字、boolean布尔值、undefined未定义、null空值、bigint大数、symbol不变值。

2、复杂数据类型

object对象、array数组、function函数、date日期、regexp正则、map键值对集合、set唯一值集合。

二、什么是symbol?

symbol表示第一无二唯一的值,它有几个特性:

1、唯一性

每次调用symbol函数都会创建一个唯一的,独一无二的值。

let sym1 = Symbol('description');
let sym2 = Symbol('description');
console.log(sym1 === sym2); // 输出: false

2、不可修改性

symbol类型的值是不可变的,一旦创建就不能修改。

3、隐式属性

symbol 类型的值可以用作对象的属性名,这些属性不会出现在对象的枚举属性中,也不会被 for...in 循环或 Object.keys 方法枚举。

let sym = Symbol('description');
let obj = {};
obj[sym] = 'value';for (let key in obj) {console.log(key); // 不会输出 sym
}console.log(Object.keys(obj)); // 输出: []

三、什么是浅拷贝什么是深拷贝?

1、浅拷贝

浅拷贝是指对基本数据类型的拷贝,和对复杂数据类型的地址的拷贝,比如Object.assign()和拓展运算符...进行的对象拷贝都是对象的浅拷贝,基本数据类型是存储在内存的栈中,复杂数据类型则是存储在堆中,而栈只是存储了这个数据的地址,所以浅拷贝复杂数据类型只是拷贝了它的地址,一处修改多处同步。

2、深拷贝

深拷贝是指对复杂数据类型的值进行拷贝,主要的深拷贝方法有:

Ⅰ、        ;

原理是将对象转化成json字符串,再将json解析成新的对象,因为 JSON 字符串是对象的完整表示,包括其所有嵌套属性。

这个方式有几个缺陷:

①、不支持函数

JSON.stringify 不会处理函数,函数在转换为 JSON 字符串时会被忽略。

let original = {name: 'Alice',sayHello: function() {console.log('Hello!');}
};
let deepCopy = JSON.parse(JSON.stringify(original));
console.log(deepCopy.sayHello); // 输出: undefined

②、不支持undefined

JSON.stringify 不会处理 undefined 值,undefined 在转换为 JSON 字符串时会被忽略。

let original = {name: 'Alice',age: undefined
};
let deepCopy = JSON.parse(JSON.stringify(original));
console.log(deepCopy.age); // 输出: undefined

③、不支持symbol

JSON.stringify 不会处理 symbol 类型的值,symbol 在转换为 JSON 字符串时会被忽略。

let original = {name: 'Alice',[Symbol('id')]: 123
};
let deepCopy = JSON.parse(JSON.stringify(original));
console.log(deepCopy[Symbol('id')]); // 输出: undefined

④、不支持循环引用

JSON.stringify 不会处理循环引用的对象,循环引用的对象在转换为 JSON 字符串时会抛出错误。

let original = {};
original.self = original;
try {let deepCopy = JSON.parse(JSON.stringify(original));
} catch (error) {console.error('循环引用错误:', error);
}

Ⅱ、递归函数

自己写递归函数。

function deepCopy(obj, visited = new WeakMap()) {if (obj === null || typeof obj !== 'object') {return obj;}if (visited.has(obj)) {return visited.get(obj);}let copy;if (Array.isArray(obj)) {copy = [];visited.set(obj, copy);obj.forEach((item, index) => {copy[index] = deepCopy(item, visited);});} else {copy = {};visited.set(obj, copy);Object.keys(obj).forEach(key => {copy[key] = deepCopy(obj[key], visited);});}return copy;
}

Ⅲ、第三方库

使用第三方库(如 lodash 的 _.cloneDeep 方法)进行深拷贝。

const _ = require('lodash');
let deepCopy = _.cloneDeep(original);

四、vue2的生命周期?

1、beforeCreate

  • 在实例初始化之后,数据观测(data observer)和事件/侦听器(event/watcher)尚未设置之前被调用。

2、ceated

  • 在实例已经完成数据观测(data observer)、属性和方法的运算、事件/侦听器的设置之后被调用,但挂载(mounting)还没开始,$el 属性目前不可用。

3、beforeMount

  • 在挂载开始之前被调用:相关的 render 函数首次被调用。

4、mounted

  • el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。

5、beforeUpdate

  • 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。

6、updated

  • 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
  • 当这个钩子被调用时,组件 DOM 已经更新,所以你可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或侦听器 (watcher) 而不是此钩子。

7、beforeDestroy

  • 实例销毁之前调用。在这一步,实例仍然完全可用。

8、destroyed

  • 实例销毁之后调用。调用后,Vue 实例指示的所有绑定都会解除,所有的子实例也会被销毁。

9、activated

  • keep-alive 组件激活时调用。

10、deActivated

  • keep-alive 组件停用时调用。

五、vue2中父子组件的生命周期调用顺序

遵循341法则,即父组件beforeCreate、父组件created、父组件beforeMount、子组件beforeCreate、子组件created、子组件beforeMount、子组件mounted、父组件mounted顺序。

六、vue3的生命周期

对比vue2的生命周期,vue3把befroeCreate和created变成了setup,其余的生命周期在前面加on就行。

七、vue3对比vue2的变化

1、Options选项式API和Composition组合式API

vue3依然兼容vue2的选项式API写法,data、methods、computed、watch分类来写,数据多了会频繁滚动,而组合式API把一个数据的声明和相关方法都集中到一块,便于管理。

2、响应式

vue2:

  • 使用 Object.defineProperty 来实现响应式系统。
  • 只能对基本数据类型和对象的直接属性进行响应式监听。

vue3:

  • 使用 ES6 的 Proxy 来实现响应式系统。
  • 可以对对象的深层次属性和数组进行响应式监听。
  • 提供了 ref 和 reactive 两种方式来定义响应式状态。

3、对于typeScript的支持

vue3对于typeScript的支持更加强大,不需要vue2使用ts的额外配置。

4、根元素的支持

vue2不允许多个根元素的出现,必须用一个div去包裹同级的多个div,而vue3允许template下面多个同级div。

5、新增指令

  • 引入了新的 API 和工具,如 Teleport 组件用于将组件的内容渲染到 DOM 树的其他位置。
  • 提供了 Suspense 组件用于处理异步组件的加载状态。
  • 引入了 Fragment,允许组件的模板返回多个根元素。
<!-- Teleport -->
<template><teleport to="body"><div>This is a teleported content</div></teleport>
</template><!-- Suspense -->
<template><suspense><template #default><async-component /></template><template #fallback><div>Loading...</div></template></suspense>
</template><!-- Fragment -->
<template><><p>First element</p><p>Second element</p></>
</template>

八、组合式API中的ref和reactive是什么?

  • ref:用于创建单个响应式值,适用于简单的响应式状态管理。可以存储任何类型的值,并在模板中自动解包。
  • <template><div><p>{{ message }}</p><button @click="updateMessage">更新消息</button></div>
    </template><script>
    import { ref } from 'vue';export default {setup() {// 使用 ref 创建一个响应式变量const message = ref('Hello, Vue 3!');// 定义一个方法来更新消息const updateMessage = () => {message.value = '消息已更新!';};// 返回需要在模板中使用的变量和方法return {message,updateMessage};}
    };
    </script>
    

  • reactive:用于创建包含多个响应式属性的对象,适用于复杂的响应式状态管理。所有嵌套属性都是响应式的,但只能用于对象。
  • <template><div><p>{{ user.name }}</p><p>{{ user.age }}</p><button @click="updateUser">更新用户信息</button></div>
    </template><script>
    import { reactive } from 'vue';export default {setup() {// 使用 reactive 创建一个响应式对象const user = reactive({name: 'Alice',age: 25});// 定义一个方法来更新用户信息const updateUser = () => {user.name = 'Bob';user.age = 30;};// 返回需要在模板中使用的变量和方法return {user,updateUser};}
    };
    </script>
    

九、什么是闭包?

简单来说,闭包是一个函数内嵌套并返回一个函数,这个嵌套的函数使用了外部函数的变量就形成了闭包。

  • 优点:

        不会对全局变量造成污染,因为它用的是函数内声明的私有变量;

        让数据私有化并可以返回出去外部操作,但这其实是函数的特性,在函数中声明变量就是在私有化变量,return就是在输出一个返回值;

        可以让这个私有变量长时间存在于内存中,避免了这个私有变量被垃圾回收机制自动销毁;

  • 缺点:

        由于私有变量不会被垃圾回收机制自动销毁,可能会造成内存泄漏;

        需要手动销毁闭包;

function fn() {let a = 10;return function() {a++;console.log(a)}
}let f = fn();
f();
f();
f();
f = null;
// 这里的fn()就是一个外部函数,声明了一个私有变量a并赋值为10,并且返回了一个内部函数,这个内部函数操作变量a自增,这就是闭包
// 由于函数fn()是有返回值的,所以声明一个f来接收fn()的返回值,此时这个f就是fn()的内部函数,再调用3次f(),输出结果是11,12,13
// 这表明了a变量没有被自动销毁,依然留存在内存中,下次调用不会使a恢复到初始值,所以记得要手动销毁闭包来释放变量a的内存占用,即f=null

值得一提的是,闭包可以用在创建工厂函数上,来生成具有特定行为的函数:

function createMultiplier(multiplier) {return function(num) {return num * multiplier;};
}const double = createMultiplier(2);
const triple = createMultiplier(3);console.log(double(5)); // 输出: 10
console.log(triple(5)); // 输出: 15

十、什么是垃圾回收机制?

垃圾回收机制是 JavaScript 引擎用来自动管理内存的一种机制。它的主要任务是找到不再使用的对象并释放其占用的内存,以避免内存泄漏。

两种常见的垃圾回收算法:

1、标记-清除算法

  • 标记阶段:从根对象(如全局对象)开始,递归地标记所有可达的对象。
  • 清除阶段:清除所有未标记的对象,释放其占用的内存。

2、引用计数算法

        这种算法通过计数每个对象的引用数来管理内存。当引用计数为零时,对象将被回收。

垃圾回收机制不会对闭包用到的变量内存进行回收。

十一、computed和watch的区别?

1、computed:

  • 适用于一个变量由其他变量计算得来,依赖的的数据变化会自动重新计算
  • 不能异步,computed有一个return是同步返回的
  • 支持缓存,在依赖数据没改变的时候返回的是缓存的数据
  • 在组件创建时立即监听其依赖的数据
  • 自动处理深度依赖,依赖的对象内部属性发生变化,computed属性也会重新计算

2、watch

  • 适用于主动监听某个属性的变化来进行其他的一些操作
  • 支持异步
  • 不支持缓存,只要监听数据发生变化就会进行相应的操作
  • 默认在第一次加载时不监听数据变化,除非设置了immediate: true
  • 默认情况下不监听对象内部属性的变化(浅监听)。如果需要监听对象内部属性的变化,需要设置deep: true

十二、组件间通信的方式?

  1. props和$emit
  2. ref和$parent
  3. eventbus即$emit和$on
    1. EventBus.$emit发送数据
    2. EventBus.$on接收数据
  4. $attrs和$listeners
    1. attrs用于接收父组件传递的所有非 props 属性。
    2. listeners用于接收父组件传递的所有事件监听器。
  5. provide和inject
    1. 数据类型provideinject 适用于非响应式数据。如果需要提供响应式数据,可以使用 Vue.observablereactive(在 Vue 3 中)
    2. 命名冲突:在多个祖先组件中提供相同名称的数据时,后代组件会注入最近的祖先组件提供的数据
    3. 性能provideinject 在大型应用中可能会影响性能,因为它们会在组件树中进行深度遍历
  6. Vuex 或 Pinia

十三、什么是vuex和pinia?

Vuex 和 Pinia 都是用于 Vue.js 应用的状态管理库

vuex:

  1. State:存储应用的状态数据。
  2. Getters:用于从 State 中派生出一些状态,类似于计算属性。
  3. Mutations:用于同步更新 State 的方法,必须是同步的。
  4. Actions:用于提交 Mutations 的方法,可以包含异步操作。
  5. Modules:用于将 Store 分割成模块,以便更好地管理复杂的状态。

pinia:

  1. Store:存储应用的状态数据。
  2. State:存储状态数据。
  3. Getters:用于从 State 中派生出一些状态。
  4. Actions:用于更新 State 的方法,可以包含同步和异步操作。

十四、v-if和v-show的区别?

v-if会让dom元素增加或删除。

v-show是给dom元素增加一个display: none来控制显隐,dom元素本身还存在。

v-if为ture时才会渲染,是真正的条件渲染。

v-show只是操作css的显隐。

v-if会触发相应组件的生命周期。

v-show不会触发生命周期。

十五、v-if 和 v-for 为什么不建议放在一起使用

在vue2中:

        因为v-for的优先级比v-if的优先级高,所以每次渲染时都会先将列表渲染出来,再通过条件判断进行显示隐藏,所以将v-ifv-for用在一起会特别消耗性能。

在vue3中:

        v-ifv-for的优先级更高。这意味着v-if的条件将无法访问到v-for作用域内定义的变量别名。

十六、为什么data是一个函数而不是一个对象?

  • 组件的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一分新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
  • 而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。

十七、vue的单项数据流是什么?

数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。这样可以防止从子组件意外改变父组件的状态,从而导致你的应用的数据流向难以理解。

在子组件直接用 v-model 绑定父组件传过来的 props 这样是不规范的写法,开发环境会报警告。

如果实在要改变父组件的 props 值可以再data里面定义一个变量,并用 prop 的值初始化它,之后用$emit 通知父组件去修改。

十八、待续


http://www.mrgr.cn/news/80061.html

相关文章:

  • 力扣题目 - 935. 骑士拨号器
  • 【汇编】思考汇编中的两个基本问题
  • STM32F407+LAN8720A +LWIP +FreeRTOS ping通
  • c# 协变与抗变
  • 蓝桥杯我来了
  • 【1211更新】腾讯混元Hunyuan3D-1文/图生3D模型云端镜像一键运行
  • 微服务篇面试题
  • 案例讲解自然语言处理(NLP)
  • 【从零开始入门unity游戏开发之——C#篇03】变量和常量
  • SpringBoot3集成MybatisPlus3和knife4j(swagger3兼容增强版)
  • C语言,有关const
  • Prime2_解法二:openssl解密凭据
  • tcpdump编译
  • uboot移植网络驱动过程,无法ping通mx6ull和ubuntu问题解决方案
  • C++小白实习日记——Pollnet,Efvi,UDP,数据类型转换(下)
  • 【Spark】Spark性能调优
  • GNSS误差源及差分定位
  • Elasticsearch Java Api Client中DSL语句的查询方法汇总
  • 防火墙端口跑不满速度处理
  • 【中工开发者】鸿蒙商城app