面试考点复盘(二)

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

相关推荐
每天进步一点点km6 小时前
自用-牛客网-华为机试编程模拟题1-python
华为od·华为·面试
frjc11 小时前
分布式服务拆分实战指南:从底层逻辑到面试话术
微服务·面试·职场和发展
考研保研资料分享12 小时前
2025生科保研北大直博:英语摘要、科研PPT与赶场复盘
面试
香蕉大香蕉(06敬畏市场)12 小时前
MySQL 单点详解(01)
java·数据库·mysql·面试·博客·实习
ShineWinsu12 小时前
对于Qt:QCheckBox常见属性的详细解析
c++·qt·面试·笔试·gui·qcheckbox·多选框
师夷长技以自愈12 小时前
第 3 讲:多 Agent——Supervisor 分诊 + 专职 Worker
面试
怕浪猫13 小时前
我有个朋友,工作八年,公司从 300 人减到 80 人
面试·程序员
考研保研资料分享13 小时前
2024财会跨保北大金融科技:三场笔面试与经管法混申复盘
科技·面试·金融
H_BB14 小时前
面试笔记:数据加密和心跳机制
笔记·面试·职场和发展
j7~15 小时前
【C++ 面试笔记】(篇二):类与数据抽象 & STL 容器与算法
开发语言·c++·面试·stl容器和算法·类和数据抽象