Skip to content
返回

2025-08-21面试

1

目录

点击展开

8.21 面试

web安全

白话:比如后台管理系统登录密码采用md5加密, 为每个接口请求头添加token, 网站使用https协议增加安全请求, 将重要信息添加到cookie里面较为隐蔽

vue2 vue3区别

白话:首先在代码风格上vue2是选项式(Options API)、vue3是组合式(Composition API), 然后就是响应式不同 vue2 采用Object.defineProperty vue3采用 es6版本Proxy代理的方式 弥补了vue2增加、删除丢失响应式问题, 还有就是虚拟 DOM 在vue3上提升了性能,生命周期不同以及 对ts的支持度 vue3用ts重写 更好的支持

本地存储有哪些?本地存储满了前端怎么处理?

白话:存储有sessionStorage、localStorage、cookie、indexDB这些, 满的话首先考虑业务本身有啥问题为啥存的超出了 是否可以让后端来返回

webpack 优化怎么优化

白话:首先利用插件进行代码压缩(TerserPlugin)、 Tree shaking打包时剔除无用代码(optimization.sideEffects)、 利用CDN加速减轻服务器压力、 图片压缩(image-webpack-loader)、 代码分割(SplitChunksPlugin) 按需加载(路由 / 组件懒加载)(通过import()导入)

说下promise

白话:Promise就是es6版本出的 处理异步任务的一种方式解决了回调地狱问题,比如说定时器、接口请求等需要等待的任务, promise有三种状态 等待(pending ) 完成(fulfilled) 、失败(rejected), 常用promise的then方法用于 成功或失败时执行

vue-router导航守卫

白话:有全局守卫beforeEach、afterEach; 路由守卫beforeEnter; 组件守卫beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave等

8.25 面试

vue自定义指令怎么实现?

白话:自定义指令可分为局部和全局自定执行,局部的话在每个组件的选项中可使用directives选项,全局的话直接使用Vue.directive

// 局部注册
new Vue({
  directives: {
    // 指令名称
    focus: {
      // 指令定义
      inserted: function (el) {
        el.focus()
      }
    }
  }
})
// 全局注册
Vue.directive('focus', {
  inserted: function (el) {
    el.focus()
  }
})

// 使用
// <input v-focus>

vue-router它有哪几种模式?分别是怎么实现的?有什么区别 ?

白话:有两种模式一种是hash另一种是history模式,hash利用 hashchange 事件监听 url 井号后面hash值的变化进行实现,优点就是页面跳转不会与后端进行交互浏览器不会刷新体验较好,缺点就是url带井号 不美观;history是基于原生h5 History API的pushState、replaceState 实现地址的存储,监听popstate 事件实现前进或后退操作,优点就是url简单,符合传统,缺点就是需要服务端支持避免某些路径没有导致后端返回404

// hash实现原理
window.addEventListener('hashchange', () => {
  const hash = window.location.hash.slice(1); // 获取 # 后的路径
  // 根据 hash 匹配组件并渲染
});

//history实现原理
// 路由跳转(不触发页面刷新)
history.pushState({}, '', '/path');

// 监听前进/后退
window.addEventListener('popstate', () => {
  const path = window.location.pathname; // 获取当前路径
  // 根据 path 匹配组件并渲染
});

文章理解:https://blog.csdn.net/poppingJ/article/details/145650829

css选择器的优先级了解吗?

白话:优先级是根据每个选择器的权重来定义的,权重大优先级越高,就先应用改选择器下的样式,从大到小排列,首先是!important, 行内样式选择器,Id选择器,然后就是类选择器,标签选择器,通配符选择器

防抖的具体的代码怎么写?

防抖的含义就是让某个时间期限内(约定200毫秒),事情处理函数只执行一次。

  // 防抖
  function debounce(func,wait){
    let timeout = null //借助闭包
    return function() {
      if(timeout) clearTimeout(timeout) 
      timeout = setTimeout(() => {
        func();
      }, wait); // 简化写法
    }
  }

Promise和setTimeout谁先执行?

白话:在js事件循环中Promise属于微任务,setTimeout属于宏任务,Promise先执行

https://juejin.cn/post/6844903512845860872

vue-router路由跳转传参两种不同的模式分别有什么优缺点?

白话:有params和query模式,params通过配置动态路由在url上以斜杠方式拼接,比较简单符合传统url地址,query是以将参数拼接在url问号后面 以key=value的形式传递,url地址较长

Vue2Vue3的响应式原理的不同分别是什么?

白话:vue2采用Object.defineProperty,为data选项中的每个属性添加getter和setter,采用递归实现性能较差,无法监听新增或删除属性;vue3则使用es6出的都Proxy,Proxy可以监听对象的所有操作,缺点就是兼容性差ie浏览器不支持

9.3面试

浏览器缓存机制

白话:浏览器缓存分为强缓存和协商缓存,强缓存就是第一次请求资源从服务器获取 如果再次获取相同资源直接拿缓存中的,通过设置请求头Cache-control属性设置相应时间即可缓存 设置no-cache 不缓存;协商缓存就是第一次和第二次请求相同资源会进行对比看资源内容是否修改,修改了就重新获取,未修改就拿本地缓存的,通过请求头Last-Modified实现

js数据类型

白话:分为基本数据类型、引用数据类型,基本数据类型有string、number、boolean、null、undefined、symbol、bigint;引用数据类型有Object、Array、Function

es6新增的数据类型了解吗什么作用

白话:有Symbol、Bigint、Map、Set;symbol表示不可变独一无二的数据,bigint表示超过number的大整数,Map用于存储键值对的集合、键可以是任意类型,Set用于存储无重复的有序列表 常用于去重操作

let var const 什么区别

白话:首先var在函数中才有作用域,let和const都有块级作用域,var具有变量提升let、const没有,var同名可以声明多次 let、const不可以,let用于声明变量、const用于声明常量,const一但定义必须给初始化值

css选择器优先级

白话:优先级是根据每个选择器的权重来定义的,权重大优先级越高,就先应用改选择器下的样式,从大到小排列,首先是!important, 行内样式选择器,Id选择器,然后就是类选择器,标签选择器,通配符选择器

样式穿透 如果私有样式不允许穿透怎么设置?

白话:在vue组件中给style标签设置scoped样式隔离的,可以使用 >>> 和/deep/ 适用于vue2、::v-deep适用于vue3已经废弃改用:deep()

js事件循环机制

白话:首先js代码分为同步和异步任务,异步任务又分为宏任务队列、微任务队列,代码执行时先执行同步再执行异步,整体代码执行完会划分出异步的微任务和宏任务代码,然后看看微任务队列有没有 有 就先拿到主线程执行微任务 然后在执行宏任务,执行完宏任务 又会划分出微任务宏任务,然后会进行新一轮的执行


宏任务:script,setTimeout,setInterval,setImmediate,I/O,UI-rendering(ui渲染) 微任务:promise.then(),MutationObserver

console.log("stard")           //第一个输出stard
async function async1(){     //async 相当于new Promise,构造函数属于同步代码
    await async2()
    console.log("saync1 end"); 
}
async function async2(){
    console.log("saync2 end");  //第二个输出saync2 end
}
async1()
setTimeout(function(){     
    console.log("setTimeout");
},0)
new Promise((resolve)=>{
    console.log("promise");  //第三个输出promise
    resolve()
})
.then(()=>{                 
    console.log("then1");
})
.then(()=>{                 
    console.log("then2")
})
console.log("end");      //第四个输出end

stard saync2 end promise end saync1 end then1 then2 setTimeout

重绘重排什么区别

白话:重绘就是元素样式发生变化但位置没变,重排也叫做回流 就是元素位置发生了改变,所以说重绘不一定重排 重排一定会重绘

pinia了解吗

白话:pinia是vue3推荐的状态管理库,对比vuex 这个更轻量化使用简单,其核心模块只有state、getters、actions,state用于存储状态数据,getters相当于 vue中的计算属性用于对state中的数据进行计算,actions用于业务处理、支持同步不或异步操作state

ts 了解吗 比如说这个类型有可能是string 有可能是number,怎么定义ts?

白话:ts就是js的一个超集,在js之上添加静态类型检查,提高代码的可维护性和安全性; 可以采用联合类型 type snT = string | number

手敲二分法查找算法

/**
 * 在有序数组中使用二分查找法查找指定值的位置。
 * 
 * @param {number[]} list 有序的数字数组
 * @param {number} value 要查找的目标值
 * @returns {number} 如果找到则返回目标值在数组中的索引,否则返回 -1
 * 
 * @example
 * // 示例:查找数字 7 在数组中的位置
 * const arr = [1, 3, 5, 7, 9, 11];
 * const index = binarySearch(arr, 7); // 返回 3
 * 
 * @example
 * // 示例:查找数组中不存在的数字
 * const arr = [2, 4, 6, 8];
 * const index = binarySearch(arr, 5); // 返回 -1
 */
function binarySearch(list, value) {
  let low = 0;    // 左端点
  let high = list.length - 1;   // 右端点
  let position = -1;
  let found = false;
  let mid;
 
  while (found === false && low <= high) {
    mid = Math.floor((low + high)/2);
    if (list[mid] == value) {
      found = true;
      position = mid;
    } else if (list[mid] > value) {  // 如果在左半部分
      high = mid - 1;
    } else {  // 如果在右半部分
      low = mid + 1;
    }
  }
  return position;
}

![[../../../assets/images/2025/Pasted image 20250905115600.png]]

9.17面试

// 快手面试一共有四面,我们这是一面,
// 一面基础的知识
// 二面深度广度
// 三面上级领导面试
// 四面 薪资等

// 先自我介绍下吧
// 了解使用过ts吗

1
var a = 1
function Fun(){
  console.log(a)
  var a= 2
}
Fun(3,4)

2
console.log(typeof typeof typeof '123'.split('') instanceof Array) // false

3
watch(source, callback, {
  immediate?: boolean // 默认:false
  deep?: boolean | number // 默认:false
  once?: boolean // 默认:false (3.4+)
  flush: 'pre' // 默认值 pre,post,sync
})

- 'pre'默认值表示在组件更新之前同步调用响应式依赖这意味着
  一旦检测到依赖的响应式数据发生变化Vue会立即执行相关的回调函数然后再进行组件的更新
- 'post'表示在组件更新之后异步调用响应式依赖这意味着
  Vue会先完成组件的更新然后再在下一个事件循环中执行回调函数
- 'sync'表示强制同步调用响应式依赖就是在组件更新之前立刻调用侦听器回调函数和更新视图函数
  这是一个比较特殊的选项通常用于需要立即响应数据变化的场景

4
const count = ref({
  a:0
})
watch(count, (c)=>{
  console.log(c.a)
})
count.value.a = 1

const count = reactive({
  a:0
})
watch(count, (c)=>{
  console.log(c.a)
})
count.a = 1


6
setTimeout(()=>{
   console(444)
},0)
new Promise((resolve)=>{
  console.log(111)
  resolve(true)
}).then(()=>{
  console.log(222)
})  

setImmediate(()=>{
  console.log(333)
})

7git 如何管理代码

// 拉代码 创建分支  提交代码 推送
git pull // 拉代码
git branch branchName // 创建分支

git add . // 存入暂存区
git commit -m "修改xxx" // 提交代码

git push // 推送

8i++ ++i  的区别

i++先使用后增加1
++i先增加1后使用

for(var i=0  i<3; i++){
   console.log(i)
}

9
'abcabc'.replace(/c/,'h') // 把第一个c替换成h
'abcabc'.replace(/c/g,'h') // 把所有的c替换成h

10
// 实现deepClone,并成功运行代码
function deepClone(target, map = new WeakMap()) {
  // 处理 null 和 基本类型
  if (target === null || typeof target !== 'object') {
    return target;
  }

  // 处理 Date
  if (target instanceof Date) {
    return new Date(target);
  }

  // 处理 RegExp
  if (target instanceof RegExp) {
    return new RegExp(target);
  }

  // 处理循环引用
  if (map.has(target)) {
    return map.get(target); // 返回已拷贝的引用
  }

  // 获取构造函数,保留原型
  const clone = Array.isArray(target) ? [] : Object.create(Object.getPrototypeOf(target));
  map.set(target, clone); // 缓存当前对象,防止循环引用

  // 递归拷贝所有可枚举属性(包括 Symbol)
  const symbols = Object.getOwnPropertySymbols(target);
  [...Object.keys(target), ...symbols].forEach(key => {
    clone[key] = deepClone(target[key], map);
  });

  return clone;
}

10.16面试

手写js数组去重


const arr = [3, 7, 9, 1, 3, 2, 1, 1, 7]

// 1、使用Set数据结构特性+ ...扩展运算符  或者使用 Array.from将Set结果转为数组
function removeRepeat1(arr) { 
	return [...new Set(arr)]
	// return Array.from(new Set(arr))
}


// 2、使用for循环 + indexOf 或者数组的 includes()方法
function removeRepeat2(arr) {
  let new_arr = []
  for (let i in arr) {
    let item = arr[i]
    if (new_arr.indexOf(item) === -1) { 
	    new_arr.push(item)
    }
  }
  return new_arr
}

// 3、使用数组的filter方法
function removeRepeat3(arr) {
  return arr.filter((value, index) => {
    return arr.indexOf(value) === index;
  });
}


// ...

手写js数组求和

const arr = [1, 2, 3, 4, 5];
const num = arr.reduce(function (a, b) {
  return a + b;
});
console.log(num); // 15

重点:数组的reduce方法使用方式

格式: reduce(callbackFn, ?initialValue)
概念:reduce方法通过迭代数组元素将其归纳为单个值,适用于数据聚合、转换及复杂计算等场景。 参数解析

  1. callbackFn:必填, 回调函数与forEach第一个参数类似 回调函数中也有4个参数:
    • accumulator :上一次循环callbackFn这个回调返回的结果
    • currentValue:当前循环的元素,与forEach中的item一样
    • currentIndex:当前循环的索引,与forEach中的inde一样
    • array:原数组
  2. initialValue:可选, 初始值

手写js防抖函数

理解:防抖,就是防止频繁重复的触发某个事件。比如当我们点击一个按钮触发一个事件时,可能会连续点了几次,那么会连续多次触发事件函数,这是一种浪费,尤其是对于异步请求数据,连续的请求数据很大程度会影响性能和用户体验。

白话:就是在触发一次函数后规定时间内没有再次触发,再执行该事件函数,若多次在规定时间内重复触发,只执行最后一次触发

/**
 * 防抖函数 - 用于限制函数的执行频率
 * @param {Function} fn - 需要进行防抖处理的函数
 * @param {number} delay - 延迟执行的时间间隔(毫秒)
 * @returns {Function} 返回一个经过防抖处理的函数
 */
function debounce(fn, delay) {
  // 声明定时器变量,用于存储setTimeout的返回值
  let timer = null;
  
  // 返回一个包装后的函数
  return function() {
    // 保存当前函数调用时的上下文(this)
    const context = this;
    // 保存当前函数调用时传入的参数(arguments)
    const args = [...arguments];
    
    // 如果之前已经设置过定时器,则清除它
    // 这样可以确保之前的延迟执行被取消
    if (timer) {
      clearTimeout(timer);
    }
    
    // 设置新的定时器,在指定延迟时间后执行函数
    timer = setTimeout(() => {
      // 使用原始上下文和参数执行目标函数
      fn.apply(context, args);
    }, delay);
  };
}

// 使用
function add(){
	this.innerHTML = ++count        
}
btn.addEventListener('click', debounce(add,1000))

手写js节流函数

理解:比如说一个input框绑定了input输入事件,如果不做任何处理,用户每次敲击键盘都会出发这个事件,会产生性能问题,此时添加节流,用户无论敲击多少次键盘,事件都会在每次指定的时间后执行函数;

就好像一个水龙头控制在滴水,每隔一段时间就会有那么一滴水滴下来。

白话:就是降低一个函数的执行频率。每隔指定的时间,才执行一次函数。

/**
 * 节流函数 - 用于限制函数的执行频率,确保函数在指定时间间隔内最多执行一次
 * @param {Function} fn - 需要进行节流处理的函数
 * @param {number} interval - 执行间隔时间(毫秒)
 * @returns {Function} 返回一个经过节流处理的函数
 */
function throttle(fn, interval) {
  // 记录上一次函数执行的时间戳,初始值为0
  let startTime = 0

  // 定义节流函数内部实现
  const _throttle = function () {
    // 获取当前时间的时间戳
    const nowTime = new Date().getTime()

    // 计算还需要等待的时间 = 设定的间隔时间 - (当前时间 - 上次执行时间)
    const waitTime = interval - (nowTime - startTime)
    
    // 如果等待时间小于等于0,说明可以执行函数了
    if (waitTime <= 0) {
      // 执行传入的函数,保持原有的this指向
      fn.apply(this)
      // 更新上次执行时间为当前时间
      startTime = nowTime
    }
  }

  // 返回节流处理后的函数
  return _throttle
}

节流 vs 防抖 简单说:节流是”定期执行”,防抖是”最后执行”

手写js倒计时

/**
 * 简化版倒计时函数
 * @param {number} seconds - 倒计时秒数
 * @param {function} callback - 回调函数,参数为剩余秒数
 * @returns {object} 包含start和stop方法的对象
 */
function simpleCountdown(seconds, callback) {
  let timeLeft = seconds;
  let timer = null;

  return {
    start() {
      callback(timeLeft);
      timer = setInterval(() => {
        timeLeft--;
        callback(timeLeft);
        if (timeLeft <= 0) {
          clearInterval(timer);
        }
      }, 1000);
    },

    stop() {
      clearInterval(timer);
    }
  };
}

// 使用示例
const countdown = simpleCountdown(5, (time) => {
  console.log(`倒计时: ${time}`);
});

countdown.start();

webpack 都用了哪些loader

白话:用到了Babel Loader用于将es6转译为es5,css loader /style-loader用于处理.css文件,Sass/Less Loader 用于将.sass/.scss 、.less文件编译为css文件

详细:https://blog.csdn.net/weixin_40629244/article/details/148909486

平常都是用哪些方法去处理异常报错的

白话:通常使用try...catch...finally结构捕获并处理,异步通常使用Promise的catch方法

async function myAsyncFunction() {  
  try {  
    let result = await someAsyncFunction();  
  } catch (error) {  
    console.error('异步错误:', error);  
  }  
}

function myAsyncFunction() { 
  someAsyncFunction().then((result) => {
	  
  }).catch((error) => {
	  console.error('异步错误:', error); 
  });   
}

在什么场景下适合使用 Promise.race() 方法?

白话:在请求超时控制中,请求接口 先成功的 先使用, 将请求超时的接口存放然后进行回滚请求,还可以多个数据源竞争,哪个数据先请求成功就使用哪个数据


Share this post on:

上一篇文章
Go语言基础
下一篇文章
为什么你的TypeScript项目里,总会有几个.d.ts文件?