前端面试准备-1
1.NodeJS的优缺点
优点:
· 高并发(最重要的优点)
· 适合I/O密集型应用
缺点:
· 不适合CPU密集型应用;CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起;
解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起;
· 只支持单核CPU,不能充分利用CPU
· 可靠性低,一旦代码某个环节崩溃,整个系统都崩溃
原因:单进程,单线程
解决方案:(1)Nnigx反向代理,负载均衡,开多个进程,绑定多个端口;
(2)开多个进程监听同一个端口,使用cluster模块;
· 开源组件库质量参差不齐,更新快,向下不兼容
· Debug不方便,错误没有stack trace
2.文件上传的优化
- 文件压缩。对图片、视频等资源,使用前端压缩(如使用 compressorjs)减少上传体积。
- 分片上传(大文件)。将大文件拆分成多个小块分片上传。
- 并发上传控制。控制并发数(如最多同时上传 3 个文件)防止资源占用过高;使用
Promise.allSettled
、任务队列或限制上传并发工具(如p-limit
)。
3.切片上传的要点
核心思想是:将大文件拆成若干小片段分批上传,最终由后端合并还原成完整文件。
- 切片策略:切片大小设置,然后使用 File.slice() API 切割
- 文件唯一标识(fileId):为了正确合并和续传,需要为文件生成唯一标识(通常基于文件名 + 文件大小 + hash)
- 上传控制逻辑:顺序或并发上传切片,通常采用 并发上传(5~10 并发) 提高性能;同时携带元信息,每个切片应携带fileId、当前分片索引(chunkIndex)、分片总数(totalChunks)、是否为最后一块、可选的 MD5 校验值
- 后端配合
4.ES6 特性
①:let和const
-
let
: 块级作用域,允许变量重新赋值 -
const
: 块级作用域,不允许重新赋值(但对象内容可变)
②:symbol
Symbol是ES6中引入的一种新的基本数据类型,用于表示一个独一无二的值,不能与其他数据类型进行运算。它是JavaScript中的第七种数据类型,与undefined、null、Number(数值)、String(字符串)、Boolean(布尔值)、Object(对象)并列。
③:模板字符串
- 在ES6之前,处理模板字符串:通过“\”和“+”来构建模板
- 对ES6来说:用
${}
来界定;反引号(``)
直接搞定;
<script>url="x"// es6之前let html="<div>"+" <a>"+url+"</a>"+"</div>";//es6let eshtml=`<div><a>${url}</a></div>`
</script>
④:解构表达式
解构赋值是对赋值运算符的扩展。它是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。字符串、以及ES6新增的Map和Set 都可以使用解构表达式
//数组解构let [a,b,c] = [1,2,3];
console.log(a,b,c); //1,2,3let [a,b,c] = [1,,3];
console.log(a,b,c); //1,undefined,3let [a,,b] = [1,2,3];
console.log(a,b);//1,3let [a,..b] = [1,2,3]; //...是剩余运算符,表示赋值运算符右边除第一个值外剩余的都赋值给b
console.log(a,b);//1,[2,3]//对象解构
let obj = { name: "山里", age: 18, sex: "m"
};let { name, age, sex } = obj;
console.log(name, age, sex); //'山里' 18 'm'let { name: myName, age: myAge, sex: mySex } = obj; //自定义变量名
console.log(myName, myAge, mySex); //'山里' 18 'm'
⑤:Map和Set属于es6新增加的对象
- Map对象用于保存键值对,任何值JavaScript支持的值都可以作为一个键(key)或者一个值(value)。
- Set对象和Map对象类似,但它存储不是键值对。类似数组,但它的每个元素都是唯一的。
const set = new Set([1, 2, 2, 3]); // 去重
const map = new Map([['a', 1], ['b', 2]]);
⑥:函数增强
- 默认参数
//默认参数
function greet(name = '游客') {console.log(`你好,${name}`);
}
-
箭头函数。箭头函数实现了一种更加简洁的书写方式。箭头函数内部没有
arguments
,也没有prototype
属性,所以不能用new
关键字调用箭头函数。
箭头函数和普通函数最大的区别在于其内部this永远指向其父级对象的this。(重点)
let add = (a,b) => {return a+b;
}
let print = () => {console.log('hi');
}
let fn = a => a * a;
//当只有一个参数时,括号可以省略,函数体只有单行return语句时,大括号也可以省略。
⑦:类(Class)
class
作为对象的模板被引入ES6,你可以通过 class
关键字定义类。class
的本质依然是一个函数。
//类的定义和继承
class Person {constructor(name) {this.name = name;}sayHi() {console.log(`Hi, I'm ${this.name}`);}
}class Student extends Person {constructor(name, grade) {super(name);this.grade = grade;}
}
⑧:模块化(Module)
优点:1.防止命名冲突;2.复用性强
// a.js
export const PI = 3.14;
export default function add(a, b) { return a + b; }// b.js
import add, { PI } from './a.js';
⑨:Promise 异步处理
const p = new Promise((resolve, reject) => {setTimeout(() => resolve('成功'), 1000);
});
p.then(result => console.log(result));
5. async-await 和 Promise的关系
async/await
和 Promise
是 JavaScript 中处理异步操作的两种方式。其中,async/await
是建立在 Promise 之上的语法糖,
特性 | Promise | async/await |
---|---|---|
语法形式 | .then() / .catch() | async 函数 + await |
可读性 | 回调链(可能嵌套过多) | 更像同步代码,结构清晰 |
错误处理 | .catch() | try...catch |
基础依赖 | 原生特性 | 构建于 Promise 之上 |
// Promise 写法
function getData() {return new Promise((resolve, reject) => {setTimeout(() => resolve('数据已获取'), 1000);});
}getData().then(res => {console.log(res);}).catch(err => {console.error(err);});
// async/await 写法
function getData() {return new Promise((resolve, reject) => {setTimeout(() => resolve('数据已获取'), 1000);});
}async function fetchData() {try {const res = await getData(); // 等待 Promise 完成console.log(res);} catch (err) {console.error(err);}
}fetchData();//await 后面跟的是一个 Promise
//async/await 实际上是对 Promise 的封装与优化
6.async-await原理
async/await
是基于 Promise
的语法糖,使得异步代码看起来像同步代码。async
函数返回一个 Promise
对象,await
表达式用于等待一个 Promise
完成。
async/await
的底层实现可以类比于生成器(Generator)函数和自动执行器。生成器函数可以在执行过程中暂停和恢复,这为实现异步流程控制提供了可能。
7.箭头函数和普通函数
特性 | 普通函数 | 箭头函数 |
---|---|---|
this 指向 | 动态绑定,由调用方式决定 | 静态绑定,取决于定义时所在作用域 |
arguments 对象 | 有 arguments 对象 | 没有 arguments ,可用 rest 参数代替 |
构造函数 | 可作为构造函数(new Fn() ) | ❌ 不可作为构造函数,不能 new |
原型 prototype | 有 .prototype 属性 | 没有 .prototype 属性 |
代码简洁性 | 相对冗长 | 更加简洁,适合写匿名函数或回调 |
能否绑定 this | 可使用 bind , call , apply 绑定 | 无效,this 永远固定(词法作用域) |
*this 指向:普通函数:调用时决定 this。
箭头函数:定义时决定 this
(继承外层作用域)
8.lodash的深拷贝怎么实现
在JavaScript中,我们经常需要复制对象。但是,JavaScript的对象复制通常只是浅复制,这意味着它只复制对象的最顶层属性,而不复制嵌套对象或数组的内部元素。这就是为什么我们有时需要深拷贝的原因。深拷贝将复制对象的所有层级,包括嵌套的对象和数组。
在使用 Lodash 时,可以通过 _.cloneDeep
方法实现 深拷贝(deep copy),这是 Lodash 提供的内置方法,用于递归地克隆一个值,包括嵌套的对象、数组等复杂数据结构。
9.对象环引用的检测
①:设置检查标记。可以给每个对象设置一个标记;或者使用 WeakSet
代替显式打标。
②:将对象引用地址存入数组,检测是否已存在。可以改进用 Set
代替数组。
10.vue中的computed和watch
①:computed
(计算属性)
-
作用:基于响应式依赖进行缓存的派生计算
-
特点:
-
返回一个值
-
仅在依赖变化时重新计算
-
用于模板中展示或复杂逻辑封装
-
-
<template><div>总价:{{ totalPrice }}</div> </template><script setup> import { ref, computed } from 'vue';const price = ref(100); const count = ref(2);// 自动依赖 price 和 count,且具备缓存 const totalPrice = computed(() => price.value * count.value); </script>
②:watch
(侦听器)
-
作用:在数据变化时执行副作用逻辑
-
特点:
-
适合执行异步操作、手动处理逻辑
-
可以侦听多个源或深层属性
-
-
<script setup> import { ref, watch } from 'vue';const searchText = ref('');// 监听输入内容变化,做防抖搜索等副作用处理 watch(searchText, (newVal, oldVal) => {console.log(`搜索内容从 "${oldVal}" 变为 "${newVal}"`);// 可调用 API 等操作 }); </script>
11.reactive
在 Vue 3 中,reactive
是 Composition API 中用于创建响应式对象的核心方法。它可以将一个普通的 JavaScript 对象转换为响应式对象,从而在数据发生变化时自动更新视图。
与 ref
的区别:
特性 | reactive | ref |
---|---|---|
用于 | 对象、数组、嵌套结构 | 原始值(字符串、数字等)或任意值 |
响应式访问 | 直接访问属性 | .value 获取/设置 |
结构深度响应 | 深层响应 | 深层对象需搭配 reactive |
// reactive 示例
const obj = reactive({ count: 1 });
obj.count++; // 自动响应视图更新// ref 示例
const num = ref(1);
num.value++; // 注意:需要 .value
12.vue2和vue3的区别
①:响应式系统的改变
Vue2 使用 Object.defineProperty
实现响应式,无法直接监听数组下标变动或对象属性的添加/删除。而 Vue3 改为使用 Proxy
,可以实现对对象任意属性的监听,性能更高、限制更少,也更易于维护。
②:组合式 API 的引入
Vue2 使用“选项式 API”,即通过 data
、methods
、computed
、watch
等分散定义组件逻辑,导致逻辑难以复用和维护。
Vue3 引入了“组合式 API”,通过 setup()
函数配合 ref
、reactive
、computed
等函数来组织逻辑,更加灵活,便于逻辑复用和 TypeScript 支持。
③:性能优化
④:TypeScript 支持增强
⑤:新特性的引入
13.模板和JSX
- JSX 是 JavaScript XML 的缩写,是 React 框架中的一种语法扩展。它允许开发者在 JavaScript 代码中直接编写类似 HTML 的语法,从而更直观地构建用户界面。
- 模板 (Template) 通常是指在前端框架中使用的一种定义 UI 结构的方式。它们通常与特定的框架绑定,例如 Vue.js 中的模板语法,Angular 中的模板语法等。
①:JSX 的优点
更强的 JavaScript 集成:JSX 是 JavaScript 的一部分,可以在 JSX 代码中编写任意 JavaScript 表达式。这个特性使得组件逻辑和视图可以紧密结合。
单文件组件:使用 JSX 时,组件的模板、逻辑和样式通常在同一个文件中,这使得组件更加自包含,易于管理和重用。
React 生态系统:JSX 是 React 的核心部分,因此使用 JSX 可以更好地利用 React 提供的各种工具和库。
②:JSX 的缺点
学习曲线:对于没有 React 经验的开发者来说,JSX 可能有一定的学习难度,尤其是在理解 JavaScript 和 JSX 之间的转换时。
可读性:虽然 JSX 使得 JavaScript 和模板代码混合在一起,但对于习惯于分离视图和逻辑的开发者来说,这种方式可能会降低代码的可读性。
③:模板的优点
分离关注点:模板语法通常将视图和逻辑分开,保持代码的清晰和可维护性。例如,在 Vue.js 中,模板负责定义视图,而逻辑和数据绑定在组件脚本部分处理。
直观的语法:模板语法通常类似于 HTML,这使得前端开发者尤其是设计师更容易上手和理解。
框架支持:模板语法通常与框架紧密集成,提供丰富的指令和绑定机制,简化了常见的 UI 操作。
④:模板的缺点
灵活性较差:由于模板语法通常是框架特定的,它们在处理复杂逻辑时可能不如 JSX 灵活。
单文件组件限制:虽然模板语法也支持单文件组件,但在某些框架中,模板、逻辑和样式分离在不同的部分,这可能会导致管理上的不便。
14.vite和Webpack
15.常用git命令
git init
初始化本地 Git 仓库- git clone <url> 克隆远程仓库到本地
- git status 查看当前工作区状态
- git add . 添加所有修改过的文件到暂存区
- git commit -m "说明" 提交暂存区到本地仓库
- git branch 查看本地分支
- git branch <分支名> 创建新分支
- git checkout <分支名> 切换分支
- git push 推送本地改动到远程
- git log 查看提交历史
16.Vue修改数据后能获取最新的dom?
在 Vue 中,修改数据后不能立即获取最新的 DOM,因为 Vue 的 DOM 更新是异步的。这意味着在你更改响应式数据后,Vue 会在下一个“tick”(事件循环的下一个微任务队列中)统一执行 DOM 更新。
正确获取最新 DOM 的方法:使用 nextTick
这是 Vue 提供的官方方式,等 DOM 更新后再执行回调。
import { nextTick } from 'vue'await nextTick()
// 或
nextTick(() => {// DOM 已更新
})
17.后端传递过来十条数据,前端需要制作一个垂直的无限循环滚动展示的效果。如何实现?
使用 CSS 动画 + JavaScript DOM 克隆机制。将列表复制一份接在原始列表之后,形成“头尾连接”。然后使用 CSS 动画持续向上滚动整个容器,当滚动到底部(即一轮滚完),立即跳回顶部,继续滚动。
<template><div class="news-wrapper"><ul class="news-list" ref="newsList"><li v-for="(news, index) in renderList" :key="index">{{ news }}</li></ul></div>
</template><script setup>
import { ref, onMounted } from 'vue'const news = ["新闻1", "新闻2", "新闻3", "新闻4", "新闻5","新闻6", "新闻7", "新闻8", "新闻9", "新闻10"
]const renderList = [...news, ...news] // 克隆一份实现无缝滚动
</script><style scoped>
.news-wrapper {height: 300px;overflow: hidden;
}
.news-list {animation: scrollUp 10s linear infinite;padding: 0;margin: 0;
}
.news-list li {height: 30px;line-height: 30px;border-bottom: 1px solid #eee;list-style: none;
}@keyframes scrollUp {0% { transform: translateY(0); }100% { transform: translateY(-50%); }
}
</style>