面试考点复盘(二)

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链传递,直到遇到.catchtry/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 继承自外层 sayNameLaterthis (即 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 则更适合监控某个数据项的变化并做出响应,特别是那些涉及到异步操作或者需要执行复杂逻辑的情况。

相关推荐
怒放吧德德1 小时前
MySQL篇:MySQL主从集群同步延迟问题
后端·mysql·面试
uhakadotcom2 小时前
MQTT入门:轻量级物联网通信协议
后端·面试·github
逆袭的小黄鸭3 小时前
JavaScript 闭包:强大特性背后的概念、应用与内存考量
前端·javascript·面试
carterwu3 小时前
如何自建一个类似antd的组件库?
面试
9号达人3 小时前
java9新特性详解与实践
java·后端·面试
uhakadotcom4 小时前
Zustand状态管理库:轻量级、高效的React解决方案
前端·javascript·面试
uhakadotcom4 小时前
阿里云STS:安全、灵活的临时访问管理
后端·面试·github
uhakadotcom5 小时前
Flutter入门指南:快速构建高性能移动应用
面试·架构·github
uhakadotcom5 小时前
React 和 Next.js 的基础知识对比
前端·面试·github
uhakadotcom5 小时前
MVC 和 MVVM 架构模式:基础知识与实践
后端·面试·架构