前端面试准备-1

发布于:2025-05-29 ⋅ 阅读:(37) ⋅ 点赞:(0)

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>";
		//es6
       let eshtml=`<div>
                   <a>${url}</a>
               </div>`
</script>

④:解构表达式

解构赋值是对赋值运算符的扩展。它是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。字符串、以及ES6新增的Map和Set 都可以使用解构表达式

//数组解构

let [a,b,c] = [1,2,3];
console.log(a,b,c);    //1,2,3
 
let [a,b,c] = [1,,3];
console.log(a,b,c);    //1,undefined,3
 
let [a,,b] = [1,2,3];
console.log(a,b);//1,3
 
let [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/awaitPromise 是 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”,即通过 datamethodscomputedwatch 等分散定义组件逻辑,导致逻辑难以复用和维护。

Vue3 引入了“组合式 API”,通过 setup() 函数配合 refreactivecomputed 等函数来组织逻辑,更加灵活,便于逻辑复用和 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>


网站公告

今日签到

点亮在社区的每一天
去签到