面试考点复盘(二)

JWT流程

  1. 前端在登录请求中将账号密码发送给后端
  2. 后端去数据库中检验账号密码,如果存在,则JsonWebToken这个第三方库将用户账号信息生成一个Token,并返回给前端
  3. 前端获取到该token后进行本地保存,并设置axios请求头,目的是保证在之后的请求过程中该token能传回给后端
  4. 在后端接口请求中,后端获取到前端传回来的token后,通过JWT检验该token的合法性,检验通过再返回前端需要的数据

深浅拷贝方法

浅拷贝

两个对象指向同一个地址

  1. Object.assign()
  2. slice()
  3. concat()
  4. 拓展运算符

深拷贝

两个对象指向不同地址

  1. _.cloneDeep()
  2. jQuery.extend()
  3. JSON.stringfy()
  4. 手写循环递归

函数缓存

函数缓存就是将函数运算过的结果进行缓存,本质上就是用空间换时间,常用于缓存数据计算结果和缓存对象

  1. 闭包
  2. 柯里化
  3. 高阶函数 -->接受其他函数作为参数或返回其他函数的函数

v-show & v-if

控制手段不同:

v-show:为元素添加css-display:none,dom元素还在

v-if:将整个dom元素添加或删除

编译过程不同:

v-show基于简单的css切换

v-if切换有一个局部的编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件

编译条件不同:

v-show由false变为true的时候,触发组件的beforeCreate、create、beforeMount、mounted钩子,由true变为false时触发组件的beforeDestory、destoryed方法

性能消耗方面: v-if有更高的切换消耗,v-show有更高的初始渲染消耗

Promise错误捕获机制

基础概念:Promise 就像快递包裹

想象你网购了一个包裹(Promise),可能有三种状态:

  1. 运输中(pending) :包裹还没到
  2. 成功(fulfilled) :包裹完好无损
  3. 异常(rejected) :包裹丢失或损坏

错误捕获就是处理「快递异常」情况的方案!

核心机制: 基于"冒泡排序"机制,错误会沿着Promise链传递,直到遇到.catch或try/catch.未被捕获的拒绝会导致全局unhandlerejection事件。

具体方法

1. .catch()链式捕获 --统一处理异常

javascript 复制代码
// 比喻:网购后统一去驿站处理问题
getPackage() // 下单
  .then(unbox)   // 拆包裹
  .then(useItem) // 使用商品
  .catch(error => { // 统一处理所有环节的问题
    console.log("包裹异常:", error);
    // 比如:重新发货、退款等
  });

特点: 无论下单、拆快递还是使用商品,都会跳到.catch() 适合场景: 链式调用多个步骤时,统一处理错误

2. async/await + try/catch --同步化处理

javascript 复制代码
// 比喻:每一步都现场检查包裹
async function handleOrder(){
    try{
        const package = await getPackage()
        const item = await unbox(package)
        await useItem(item)   // 现场使用
       }catch(error){//任何一个步骤出错直接跳转到此
            console.log("现场发现问题",error)
        }
}

特点: 用同步代码处理异步错误

适合场景: 需要逐步处理,且每一步都可能需要错误处理

  • 注意 所有需要通过 reject 传递的错误才能被捕获

3. 全局捕获

javascript 复制代码
// 比喻:快递公司总部监控所有未处理的异常
// 浏览器环境
window.addEventListener('unhandledrejection', (event) => {
  console.log("发现未处理的包裹异常:", event.reason);
  event.preventDefault(); // 阻止浏览器默认报错
});

// Node.js 环境
process.on('unhandledRejection', (error) => {
  console.log("未处理的异常:", error);
});

特点: 捕获所有未被处理的Promise错误 适合场景:防止程序因未捕获错误直接崩溃

异步回调错误处理方法

异步回调中的错误必须手动处理
setTimeout、事件监听等非 Promise 异步操作中的错误,必须用 try/catch 包裹后手动调用 reject。

方法一:在异步操作内部手动捕获
javascript 复制代码
new Promise((resolve, reject) => {
  setTimeout(() => {
    try { // 手动添加 try/catch
      throw new Error('异步错误!');
    } catch (error) {
      reject(error); // 手动触发 reject
    }
  }, 1000);
})
  .catch(error => console.log("捕获成功:", error));
方法二:将 setTimeout 包装成 Promise
javascript 复制代码
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function handleAsyncError() {
  try {
    await delay(1000);
    throw new Error('异步错误!'); // 此时错误在 Promise 链中
  } catch (error) {
    console.log("捕获成功:", error);
  }
}

handleAsyncError();

箭头函数

1. 箭头函数是什么?

箭头函数是 简化版的函数写法 ,用 => 符号定义。它能解决传统函数的两个痛点:

  1. 代码冗长:传统函数写起来麻烦
  2. this 指向混乱 :传统函数的 this 会变来变去

2.传统写法vs箭头函数

javascript 复制代码
// ----------------------------
// 传统函数
// ----------------------------
function add(a, b) {
  return a + b;
}

// 匿名函数版本
const add = function(a, b) {
  return a + b;
};

// ----------------------------
// 箭头函数(完全等效)
// ----------------------------
const add = (a, b) => {
  return a + b;
};

// 极简写法(当只有一行 return 时)
const add = (a, b) => a + b; // 自动返回计算结果

3. 特殊规则

规则1:单参数可省略括号

javascript 复制代码
// 传统写法
const square = function(n) {
  return n * n;
};

// 箭头函数简写
const square = n => n * n; //没有括号

规则2:无参数必须有括号

javascript 复制代码
// 传统写法
const sayHi = function() {
  console.log("Hello!");
};

// 箭头函数
const sayHi = () => console.log("Hello!"); //空括号

规则3:返回对象要用括号包裹

javascript 复制代码
// 错误写法
const createUser = () => { name: "John" }; // ❌ 会被认为是代码块

// 正确写法
const createUser = () => ({ name: "John" }); // ✅ 用括号包裹对象

规则4:!!this不会变!!

这一点是箭头函数与传统函数最本质的区别!

传统函数的 this 会变
javascript 复制代码
const person = {
  name: "Alice",
  sayName: function() {
    console.log(this.name); // ✅ 正常输出 "Alice"
  },
  sayNameLater: function() {
    setTimeout(function() {
      console.log(this.name); // ❌ 这里 this 指向 window(浏览器环境)
    }, 1000);
  }
};

执行流程:

  1. person.sayNameLater() 调用时,sayNameLater 内部的 this 指向 person
  2. setTimeout 的回调是一个 传统函数
  3. 当 1 秒后回调执行时,相当于直接调用 function() { ... },此时 this 指向 window(浏览器环境)
箭头函数的 this 固定
javascript 复制代码
const person = {
  name: "Alice",
  sayName: () => {
    console.log(this.name); // ❌ person对象字面量没有自己的作用域,箭头函数没有自己的 this,这里指向外层(可能是 window)
  },
  sayNameLater: function() {
    setTimeout(() => {
      console.log(this.name); // ✅ 箭头函数继承外层 this,输出 "Alice"
    }, 1000);
  }
};

执行流程:

  1. person.sayNameLater() 调用时,sayNameLater 内部的 this 指向 person
  2. setTimeout 的回调是一个 箭头函数
  3. 箭头函数的 this 继承自外层 sayNameLater 的 this (即 person)
  4. 因此回调中的 this.name 正确指向 person.name 关键结论 :
    箭头函数没有自己的 this,它的 this 永远指向定义时所在的外层作用域。

4.对比

5. 口诀

  1. 传统函数看调用 ------ 谁调用,this 就指向谁
  2. 箭头函数看定义 ------ 定义时外层是谁,this 就是谁
  3. 对象方法慎用箭头 ------ 避免 this 指向全局
  4. 回调函数优先箭头 ------ 自动锁定外层 this

常用的 Git 命令

按功能分类整理:


1. 配置相关

  • 设置用户名和邮箱

    bash 复制代码
    git config --global user.name "你的名字"
    git config --global user.email "你的邮箱"
  • 查看配置信息

    bash 复制代码
    git config --list

2. 仓库创建与克隆

  • 初始化新仓库

    bash 复制代码
    git init
  • 克隆远程仓库

    bash 复制代码
    git clone <远程仓库地址>

3. 基本操作

  • 添加文件到暂存区

    bash 复制代码
    git add <文件名>        # 添加单个文件
    git add .              # 添加所有修改和新文件
  • 提交更改

    bash 复制代码
    git commit -m "提交说明"
  • 查看仓库状态

    bash 复制代码
    git status
  • 查看文件修改差异

    bash 复制代码
    git diff              # 工作区与暂存区的差异
    git diff --staged     # 暂存区与最新提交的差异

4. 分支管理

  • 创建分支

    bash 复制代码
    git branch <分支名>
  • 切换分支

    bash 复制代码
    git checkout <分支名>     # 旧版写法
    git switch <分支名>       # 新版推荐
  • 创建并切换分支

    bash 复制代码
    git checkout -b <分支名>  # 旧版写法
    git switch -c <分支名>    # 新版推荐
  • 合并分支

    bash 复制代码
    git merge <分支名>       # 将指定分支合并到当前分支
  • 删除分支

    bash 复制代码
    git branch -d <分支名>   # 安全删除(已合并的分支)
    git branch -D <分支名>   # 强制删除(未合并的分支)
  • 查看分支列表

    bash 复制代码
    git branch          # 本地分支
    git branch -a       # 包括远程分支
  • 查看分支合并情况

    bash 复制代码
    git log --graph --oneline --all

5. 远程操作

  • 添加远程仓库

    bash 复制代码
    git remote add origin <远程仓库地址>
  • 查看远程仓库

    bash 复制代码
    git remote -v
  • 拉取远程更新

    bash 复制代码
    git fetch origin        # 仅拉取不合并
    git pull origin <分支名> # 拉取并合并(相当于 fetch + merge)
  • 推送本地提交到远程

    bash 复制代码
    git push origin <分支名>
  • 删除远程分支

    bash 复制代码
    git push origin --delete <分支名>

6. 撤销与回退

  • 撤销工作区修改

    bash 复制代码
    git checkout -- <文件名>   # 丢弃未暂存的修改
  • 撤销暂存区的文件

    bash 复制代码
    git reset HEAD <文件名>    # 取消暂存
  • 修改最后一次提交

    bash 复制代码
    git commit --amend        # 修改提交信息或内容
  • 回退到指定提交

    bash 复制代码
    git reset --hard <commit-id>  # 彻底回退到某个版本
    git reset --soft HEAD~1       # 回退提交但保留修改
  • 恢复文件到某次提交

    bash 复制代码
    git restore --source=<commit-id> <文件名>

7. 标签管理

  • 创建标签

    bash 复制代码
    git tag <标签名>          # 轻量标签
    git tag -a <标签名> -m "说明"  # 附注标签
  • 推送标签到远程

    bash 复制代码
    git push origin <标签名>
  • 删除标签

    bash 复制代码
    git tag -d <标签名>       # 删除本地标签
    git push origin --delete <标签名>  # 删除远程标签

8. 查看日志与历史

  • 查看提交历史

    bash 复制代码
    git log
    git log --oneline       # 简洁模式
    git log --graph         # 图形化分支历史
  • 查看某文件的修改历史

    bash 复制代码
    git blame <文件名>       # 显示每一行的最后修改者

9. 子模块(Submodule)

  • 添加子模块

    bash 复制代码
    git submodule add <仓库地址> <路径>
  • 更新子模块

    bash 复制代码
    git submodule update --init --recursive

常用场景示例

  1. 首次推送本地项目到远程仓库

    bash 复制代码
    git init
    git add .
    git commit -m "Initial commit"
    git remote add origin <远程仓库地址>
    git push -u origin main
  2. 同步远程最新代码

    bash 复制代码
    git fetch origin    # 拉取最新代码
    git merge origin/main  # 合并到当前分支
    # 或直接使用 pull
    git pull origin main

vue-router

核心定位

Vue Router 是 Vue 生态的官方路由解决方案,实现了 SPA 的核心路由机制。 核心解决两个问题:

  • URL 变化时无刷新更新视图
  • 保持视图状态与 URL 同步

设计模式

采用『前端路由』模式,通过两种方案实现:

  • Hash 模式 :利用 onhashchange 监听 URL 变化,兼容性好
  • History 模式 :基于 History API,需要服务端配合支持 HTML5 特性
    两者的选择需要根据项目部署环境权衡"

Router VS Route

回答模板:在Vue Route中,

  • Router是全局路由管理器,负责控制路由模式(Hash/History)、跳转逻辑(push/replace)和守卫拦截(beforeEach),通过useRouter()获取实例。
  • Route是单个路由的配置规则,定义路径与组件的映射关系,支持嵌套路由和元信息。通过useRoute()获取当前路由信息(如参数、查询字符串)。

怎么判断一个对象是否为空

方法 1:Object.keys()(最常用)

javascript 复制代码
function isEmpty(obj) {
  return Object.keys(obj).length === 0;
}

// 示例
isEmpty({});         // true
isEmpty({a: 1});     // false

缺点:

  • 不检查不可枚举属性

方法 2:JSON.stringify()(深空检查)

javascript 复制代码
function isEmpty(obj) {
  return JSON.stringify(obj) === '{}';
}

isEmpty({});         // true
isEmpty({a: undefined}); // false(注意:值为 undefined 的属性会被忽略)

适用场景:

  • 需要快速检查简单对象的深空状态
    注意:
  • 会忽略 undefined 和函数类型的值
  • 性能较差(需序列化整个对象)

方法 3:for...in 循环(最可靠)

javascript 复制代码
function isEmpty(obj) {
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) return false;
  }
  return true;
}

// 示例
isEmpty(Object.create({a: 1})); // true(不检查原型链)

优势:

  • 精确控制检查范围(可选择性包含原型属性)
  • 兼容所有 JavaScript 环境
    缺点:
  • 代码稍显冗长

方法 4:Object.getOwnPropertyNames()(包含不可枚举属性)

javascript 复制代码
function isEmpty(obj) {
  return Object.getOwnPropertyNames(obj).length === 0;
}

// 示例
const obj = Object.create(null);
Object.defineProperty(obj, 'hidden', { enumerable: false });
isEmpty(obj); // false(检测到不可枚举属性)

获取DOM的方法有哪些

一、Vue 专用方法

1. 模板 ref 属性(推荐)

js 复制代码
<template>
  <input ref="inputRef" />
</template>

<script setup>
import { ref, onMounted } from 'vue'

const inputRef = ref(null) // 创建同名ref

onMounted(() => {
  inputRef.value.focus() // 获取DOM实例
})
</script>

特点:

  • 组合式API标准用法
  • 自动处理组件更新和卸载时的引用清理
  • 支持 TypeScript 类型提示

二、原生 JavaScript 方法

1. document.getElementById()
js 复制代码
const el = document.getElementById('my-id') // 返回单个元素

适用场景:

  • 简单页面快速获取已知ID元素
  • 非响应式静态页面
2. document.querySelector()
js 复制代码
const el = document.querySelector('.class-name') // 获取首个匹配元素
const list = document.querySelectorAll('div')   // 获取所有匹配元素(NodeList)

优势:

  • 支持CSS选择器语法
  • 可获取复杂结构的元素
3. document.getElementsByClassName()
js 复制代码
const elements = document.getElementsByClassName('class-name') // HTMLCollection

注意:

  • 返回动态集合(随DOM变化自动更新)
  • 性能优于 querySelectorAll 但功能有限
4. document.getElementsByTagName()
js 复制代码
const divs = document.getElementsByTagName('div') // HTMLCollection

Q1:为什么 mounted 钩子中能获取DOM?

  • Vue 的生命周期确保 DOM 已挂载完成
  • 对于动态内容,可使用 nextTick:
js 复制代码
import { nextTick } from 'vue'
nextTick(() => {
  // 确保DOM更新后执行
})

Q2:如何获取组件内的DOM?

js 复制代码
<!-- Child.vue -->
<template>
  <div ref="inner"></div>
</template>

<script setup>
const inner = ref(null)
defineExpose({ inner }) // 暴露给父组件
</script>

<!-- Parent.vue -->
<script setup>
const childRef = ref(null)
onMounted(() => {
  console.log(childRef.value.inner) // 访问子组件DOM
})
</script>

watch 和computed区别

watch (侦听器)

设计目的:观察变化执行副作用(如异步操作)

无返回值

无缓存(每次触发都执行)

可直接处理异步

可配置immediate决定是否进行初始化触发

典型场景:数据变化时执行请求,验证等操作

computed (计算属性)

设计目的:派生状态(基于依赖计算新值)

必须返回一个值

有缓存(依赖不变时复用结果)

不能包含异步操作

初始化触发:默认立即计算

典型场景:模板中需要的数据加工

总结

computed 更适用于依赖其它数据进行计算并需要缓存的情况,能够简化模板中的逻辑表达式,使代码更加清晰。

watch 则更适合监控某个数据项的变化并做出响应,特别是那些涉及到异步操作或者需要执行复杂逻辑的情况。

相关推荐
Zelman1 小时前
测试层级与测试类型
后端·面试·测试
考研保研资料分享2 小时前
自动化控制保研经验合辑:上交夏令营、北工大预推免与跨专业面试复盘(2025)
运维·面试·自动化
怕浪猫4 小时前
Agent 工程化面试:从开发到部署的 5 个关键问题
面试·架构·github
用户6919026813395 小时前
LangChain 与 LangGraph:从「链」到「图」,一次讲清它们是什么、什么关系
面试·架构·设计
CoderYanger5 小时前
A.每日一题:856. 括号的分数
java·程序人生·算法·leetcode·面试·职场和发展·学习方法
Sam_Deep_Thinking7 小时前
CountDownLatch实现原理
java·后端·面试·程序员
范中勤7 小时前
Python 与 Java:模块、包、对象、反射核心差异总结
java·python·面试·反射·元类
晚风叙码7 小时前
MySQL 增删改查(CRUD)完全指南:从入门到面试
数据库·mysql·面试
夏幻灵16 小时前
JavaScript this 面试:五种绑定规则、优先级与常见面试陷阱
开发语言·javascript·面试
时间的拾荒人21 小时前
Qt 界面优化:绘图详解 —— 从基础图形到图片处理
开发语言·qt·面试