2024前端面试题(持续更新)
目录
一、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.parse(JSON.stringify(对象));
原理是将对象转化成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、引用计数算法
这种算法通过计数每个对象的引用数来管理内存。当引用计数为零时,对象将被回收。
垃圾回收机制不会对闭包用到的变量内存进行回收。
十一、
原文地址:https://blog.csdn.net/qq_43378240/article/details/144400092
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!