九方前端面试

Published 2025-04-19 10:47 1602 words 9 min read

做产品前,先别急着写代码:我是怎么判断一个点子值不值得做的想偷卷?但微信不支持md文档?这个软件助你!新手小白如何开发练手项目?前端常见安全问题 + 防御方法 + 面试回答吉比特(雷霆游戏)前端二面问题总结吉比特27前端实习面试纷享销客前端实习一面08-刚体约束 Constraint07-监听碰撞事件和获取碰撞信息06 - 物体休眠与休眠事件详解(Cannon.js)05-碰撞与碰撞组04 - 弹性与接触材质详解03 - Cannon 材质与摩擦系数设置02 - Cannon 引擎基础碰撞讲解01 - Cannon.js 引擎的用途和基本使用一款减轻前端图片命名工作量的图片转换器总结:一个双非大学生的两年,是怎么过来的?数学公式Input-输入框组件实现详解用HTML5实现实时ASCII艺术摄像头object-fit: contain - CSS 属性详解GSAP (GreenSock Animation Platform) 动画库学习指南Webview通信系统学习指南实现手势操控3D粒子动画?太好玩了!东田数码科技前端面试前端性能优化面试回答技巧(一)React与Vue表单的对比差异融山科技前端面经·React的入门学习(一)九方前端面试JavaScript 核心:数组/字符串 API 精要指南JavaScript的常用数组API原理解析前端练习小网站-新手突破之路前端の骚操作代码合集 (二)| 让你的网页变得有趣前端Threejs入门(五)Vue 3 响应式原理:computed的模板解包机制Vue 监听器 watch 深度解析Axios 深入解析从0到1配置vue项目跟git仓库连接!前端Three.js入门(四)你懂keep-alive吗?见过Vue的小区保安吗?前端Three.js入门(三)前端Three.js入门(二)前端Three.js入门(一)-Three的第一个简单的页面 Git 大冒险:解锁代码管理的秘密武器如何将网站加载速度提升50%?前端性能优化全攻略前端の骚操作代码合集 | 让你的网页充满恶趣味进击蓝桥杯!2025web前端HTML5uniapp-页面生命周期
This post is not yet available in English. Showing the original.
九方前端面试 作者: 前端AC | 原文: https://juejin.cn/post/7494531085428359222 面试问题回答 小编大二找实习面试,跟面试官死缠烂打的一次,公司使用的是vue2和vue3较多,所以问的比较多的是vue2和es6,以下是总结的面试官喜欢问的几个点。 ...

九方前端面试

作者: 前端AC | 原文: https://juejin.cn/post/7494531085428359222

面试问题回答

小编大二找实习面试,跟面试官死缠烂打的一次,公司使用的是vue2和vue3较多,所以问的比较多的是vue2和es6,以下是总结的面试官喜欢问的几个点。

1. ES6新特性有哪些?

核心特性

  • let/const:块级作用域变量声明

  • 箭头函数:简化函数写法,自动绑定外层this

    const add = (a, b) => a + b;
  • 模板字符串:支持多行字符串和变量嵌入

    const name = 'Alice';
    console.log(`Hello, ${name}!`);
  • 解构赋值:从数组/对象中提取值

    const [a, b] = [1, 2];
    const { name, age } = user;
  • 默认参数:函数参数默认值

    function greet(name = 'Guest') { /* ... */ }
  • 类(Class):语法糖面向对象编程

    class Person {
      constructor(name) { this.name = name; }
    }
  • 模块化import/export模块系统

  • Promise:异步编程解决方案

  • Symbol:唯一不可变数据类型

  • Set/Map:新数据结构


2. ES6数组新增了什么?

新增方法

  • 扩展运算符...展开数组

    const arr = [...[1,2], 3]; // [1,2,3]
  • Array.from():类数组转数组

    Array.from(document.querySelectorAll('div'));
  • Array.of():创建数组

    Array.of(1,2,3); // [1,2,3]
  • find()/findIndex():查找元素

    [1,2,3].find(x => x > 1); // 2
  • includes():判断包含元素

    [1,2,3].includes(2); // true
  • fill():填充数组

    new Array(3).fill(0); // [0,0,0]
  • entries()/keys()/values():遍历方法


3. Vue2的响应式原理是什么?

核心机制

  • Object.defineProperty:劫持对象属性

    function defineReactive(obj, key) {
      let value = obj[key];
      Object.defineProperty(obj, key, {
        get() {
          console.log('收集依赖');
          return value;
        },
        set(newVal) {
          console.log('触发更新');
          value = newVal;
        }
      });
    }
  • 依赖收集:通过Dep类和Watcher实现

  • 数组处理:重写数组方法(push/pop等)

    const arrayProto = Array.prototype;
    const arrayMethods = Object.create(arrayProto);
    ['push', 'pop'].forEach(method => {
      const original = arrayProto[method];
      arrayMethods[method] = function(...args) {
        const result = original.apply(this, args);
        dep.notify(); // 手动触发更新
        return result;
      };
    });
  • 局限性:无法检测到对象属性新增/删除(需用Vue.set


4. Axios的二次封装

典型封装方案

// src/utils/request.js
import axios from 'axios';

const service = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000
});

// 请求拦截器
service.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
}, error => {
  return Promise.reject(error);
});

// 响应拦截器
service.interceptors.response.use(response => {
  return response.data;
}, error => {
  if (error.response.status === 401) {
    // 处理未授权
  }
  return Promise.reject(error);
});

// 封装GET/POST
export function get(url, params) {
  return service.get(url, { params });
}

export function post(url, data) {
  return service.post(url, data);
}

5. 组件封装实践

封装流程

  1. 需求分析:明确组件功能边界

  2. Props定义:定义可配置参数

    export default {
      props: {
        type: {
          type: String,
          default: 'primary'
        }
      }
    }
  3. 插槽处理:使用<slot>实现内容分发

    <button class="my-button">
      <slot>默认按钮</slot>
    </button>
  4. 事件通信:通过$emit触发事件

    <button @click="$emit('click')">
  5. v-model支持:实现双向绑定

    model: {
      prop: 'value',
      event: 'change'
    }

6. Vue2的痛点

常见痛点

  1. 响应式限制
    • 无法检测数组索引修改:arr[0] = newVal
    • 无法检测对象属性添加/删除
  2. 代码组织
    • Mixins导致命名冲突和来源不清晰
    // mixinA.js 和 mixinB.js 可能都有同名方法
  3. TS支持弱:选项式API对TypeScript类型推断不友好
  4. 性能问题:深度嵌套对象递归劫持性能消耗大
  5. 逻辑复用:相同逻辑需要在不同组件重复实现
  6. 大型项目:随着项目增大,组件间通信复杂度上升

解决方案趋势:这些痛点促使了Vue3的Composition API诞生


7-补充:Vue2响应式对对象属性新增/删除的局限性(当时说不出来这个set)

问题背景

Vue2使用Object.defineProperty实现响应式,但其核心限制是只能劫持对象已存在的属性。这意味着:

  • 新增属性:直接通过obj.newKey = value添加的属性无法触发视图更新。
  • 删除属性:通过delete obj.key删除属性也无法触发更新。

示例场景

// Vue实例中定义的data
data() {
  return {
    user: { name: "Alice" }
  };
}

// 直接新增属性(不会触发响应式更新)
this.user.age = 25; // ❌ 视图不会更新

// 直接删除属性(不会触发响应式更新)
delete this.user.name; // ❌ 视图不会更新

问题根源

Vue2在初始化时会递归遍历data中的所有属性,并用Object.defineProperty将它们转为响应式。但后续动态添加或删除属性时:

  1. 新属性未被Object.defineProperty劫持。
  2. 删除属性不会触发setter逻辑。

解决方案:Vue.set / this.$set

通过Vue.set(或实例方法this.$set)强制让新属性具备响应性。

语法

Vue.set(target, propertyName/index, value);
// 或组件内使用:
this.$set(target, propertyName/index, value);

对象属性示例

// 正确添加响应式属性
this.$set(this.user, "age", 25); // ✅ 视图更新

// 正确删除属性(需用Vue.delete)
Vue.delete(this.user, "name"); // ✅ 视图更新

数组场景示例

Vue2对数组的以下操作无法触发更新:

// 直接通过索引修改数组
this.items[0] = "newValue"; // ❌ 视图不会更新

// 正确方式
this.$set(this.items, 0, "newValue"); // ✅ 视图更新

底层原理

Vue.set的核心逻辑:

  1. 检查目标是否为响应式对象:只有Vue管理的对象才能被处理。
  2. 添加属性并转为响应式:对新属性调用defineReactive方法(内部使用Object.defineProperty)。
  3. 手动触发依赖更新:通知所有关联的Watcher重新渲染。

Vue3的改进

Vue3使用Proxy替代Object.defineProperty,解决了这一问题:

  • Proxy可以拦截对象的所有操作(包括属性增删)。
  • 无需手动调用set/delete,动态属性天然支持响应式。

总结

操作类型Vue2的限制Vue2的解决方案Vue3的支持
对象新增属性❌ 无法检测this.$set(obj, key, value)✅ 天然支持
对象删除属性❌ 无法检测Vue.delete(obj, key)✅ 天然支持
数组索引修改❌ 无法检测this.$set(arr, index, val)✅ 天然支持

关键点:在Vue2中,必须通过Vue.setVue.delete显式处理动态属性,否则会破坏响应式。这一机制是Vue2响应式系统的核心设计局限,也是开发者常见的“踩坑点”。