2024最新前端面试真题-超详细答题思路解析(持续更新ing)

为了更加高效的准备面试,所以面试题都来源于牛客网真题,让面试题更符合实际。

1 cookie,sessionStorage,localStorage的区别

(1)考察点分析

  • 存储机制:了解每种存储方式的实现原理和数据存储方式。
  • 生命周期:掌握每种存储方式的数据存储时长和生命周期管理。
  • 作用域:理解每种存储方式的数据访问范围和作用域限制。

(2)最终答案

  1. Cookie

    • 存储机制:以文本形式存储在客户端浏览器中,通过 HTTP 头部在客户端和服务器之间传递。
    • 生命周期:可以设置过期时间,在有效期内持久保存,过期后会自动删除。
    • 作用域:可以设置路径和域名限制访问,同源策略下会自动发送到服务器端。
  2. sessionStorage

    • 存储机制:HTML5 提供的会话存储机制,仅在当前会话(浏览器标签页)下有效。
    • 生命周期:数据在页面关闭后自动清除,不同页面或标签页间数据不共享。
    • 作用域:每个页面拥有独立的 sessionStorage,不同页面之间无法共享数据。
  3. localStorage

    • 存储机制:HTML5 提供的持久化本地存储机制,数据永久保存在客户端浏览器上。
    • 生命周期:除非被显式清除,否则数据永久保存,即使浏览器关闭也不会被清除。
    • 作用域:每个域名拥有独立的 localStorage,不同页面和标签页间可以共享数据。

2 position 有哪些属性值?

(1)考察点分析

  • 基础知识:考察候选人是否理解CSS中的position属性及其各种可能的值。
  • 应用能力:考察候选人能否正确选择和使用不同的position属性值来实现所需的布局效果。
  • 问题解决能力:考察候选人是否能通过对position属性的理解,解决实际开发中的布局问题。

(2)最终答案

CSS 的 position 属性有以下几种属性值:

  • static:默认值。元素按照正常的文档流进行定位,不受 top、right、bottom、left 等属性影响。
    示例:
This is static
  • relative:相对定位。元素相对于其正常位置进行偏移,但仍保留在文档流中。
This is relative
  • absolute:绝对定位。元素相对于最近的定位祖先(非 static)进行定位,如果没有定位祖先,则相对于初始包含块(通常是浏览器窗口)。
This is absolute
  • fixed:固定定位。元素相对于浏览器窗口进行定位,即使页面滚动也不会移动。
    示例:
This is fixed
  • sticky:粘性定位。元素在相对定位和固定定位之间切换,依赖于用户的滚动位置。在元素的滚动范围内表现为相对定位,超出范围后表现为固定定位。
This is sticky

3 叙述一下不同情境下的 This 指向

(1)考察点分析:

  • 基础知识掌握:考察候选人是否理解 JavaScript 中 this 的工作原理和不同的指向情况。
  • 语言和执行上下文理解:考察候选人对 JavaScript 执行上下文的理解,包括全局执行上下文、函数执行上下文和对象方法执行上下文。
  • 调试和问题解决能力:考察候选人是否能够理解和解释代码中 this 的行为,以及解决 this 指向不明确导致的问题。

(2)最终答案

// 1. 全局上下文中的 this
console.log(this); // 输出全局对象,如 window(浏览器环境)

// 2. 函数调用中的默认绑定
function foo() {
  console.log(this);
}
foo(); // 输出全局对象,如 window(非严格模式下)

// 3. 对象方法调用中的 this
const obj = {
  name: 'John',
  greet: function() {
    console.log(this.name);
  }
};
obj.greet(); // 输出 'John'

// 4. 构造函数中的 this
function Person(name) {
  this.name = name;
}
const person1 = new Person('Alice');
console.log(person1.name); // 输出 'Alice'

// 5. 使用 call、apply、bind 改变 this 的指向
const obj2 = {
  name: 'Emily'
};
function introduce() {
  console.log(`My name is ${this.name}`);
}
introduce.call(obj2); // 输出 'My name is Emily'

// 6. 箭头函数中的 this
const obj3 = {
  name: 'Tom',
  greet: () => {
    console.log(this.name); // this 指向全局对象,因为箭头函数没有自己的 this 绑定
  }
};
obj3.greet(); // 输出全局对象的 name(如果定义了)

4 TS 泛型函数是什么?

(1)考察点分析

  • 泛型概念理解:考察候选人是否理解泛型在编程语言中的作用和基本概念。
  • 泛型函数的使用场景:考察候选人能否识别并利用适合使用泛型的场景,如处理集合类型、返回值类型不确定等。
  • 类型安全性和代码复用性:考察候选人能否通过泛型函数提高代码的可维护性、扩展性和类型安全性。

(2)最终答案

1.定义泛型函数:
泛型函数使用 或其他字母来表示类型变量,这些类型变量可以在函数体内用作参数类型或返回类型的占位符。

function identity(arg: T): T {
    return arg;
}

在上面的示例中, 表示这是一个泛型函数,T 是类型变量,它表示任意类型。函数 identity 接受一个参数 arg,类型为 T,并返回一个 T 类型的值。
2. 使用泛型函数:

// 调用泛型函数,并传入具体类型为 number
let result1 = identity(5); // result1 的类型为 number
console.log(result1); // 输出 5

// 调用泛型函数,并传入具体类型为 string
let result2 = identity("Hello"); // result2 的类型为 string
console.log(result2); // 输出 "Hello"

在这个示例中,identity(5) 和 identity(“Hello”) 分别传入了 number 类型和 string 类型的参数,TypeScript 根据传入的参数类型推导出 result1 和 result2 的具体类型。
3. 泛型函数处理数组示例:

function reverse(items: T[]): T[] {
    return items.reverse();
}

let numbers = [1, 2, 3, 4, 5];
let reversedNumbers = reverse(numbers); // reversedNumbers 的类型为 number[]
console.log(reversedNumbers); // 输出 [5, 4, 3, 2, 1]

let names = ["Alice", "Bob", "Charlie"];
let reversedNames = reverse(names); // reversedNames 的类型为 string[]
console.log(reversedNames); // 输出 ["Charlie", "Bob", "Alice"]

在这个示例中,reverse 函数是一个泛型函数,接受一个数组 items,其中的元素类型为 T。根据传入的 numbers 和 names 数组,分别推导出 reversedNumbers 和 reversedNames 的具体类型。

5 比较 Vue 2 和 Vue 3 的响应式系统有什么区别?

(1)考察点分析

  • 深入了解 Vue 框架:考察候选人对 Vue.js 框架的理解程度,特别是其核心的响应式系统。
  • 版本差异的掌握:考察候选人是否能够区分和理解 Vue 2 和 Vue 3 在响应式系统实现上的主要区别。
  • 性能优化和技术升级:考察候选人对新技术(如 Proxy)的接受和掌握程度,以及这些技术在框架升级中的应用。

(2)最终答案

  • 性能优化:Vue 3 的 Proxy 相较于 Vue 2 的 Object.defineProperty 有更好的性能,因为 Proxy 的代理操作更高效。
  • 功能增强:Vue 3 的 Proxy 支持更多的操作捕获,使得框架能够更精确地追踪响应式依赖。
  • 适应新特性:Vue 3 的响应式系统更适应现代 JavaScript 和浏览器的发展趋势,如 ES6+ 的语法特性和新的浏览器API。

6 请解释一下 CSS 盒模型及其组成部分

(1)考察点分析

  • 基本概念理解:考察候选人对 CSS 盒模型的基本理解和应用能力。
  • 属性应用能力:考察候选人是否能清晰地解释盒模型的各个组成部分及其对页面布局的影响。
  • 问题解决能力:考察候选人在实际开发中如何应用盒模型进行页面设计和调试。

(2)最终答案

  1. 盒模型概述

    CSS 盒模型是用来描述每个 HTML 元素在页面布局中所占空间的模型。它包括了元素的内容区域、内边距、边框和外边距四个部分。

  2. 组成部分详解

    • 内容区域(Content):显示元素实际内容的区域,可以通过设置 widthheight 属性控制大小。
    • 内边距(Padding):内容区域与边框之间的空白区域,可以使用 padding 属性设置,如 padding: 10px;
    • 边框(Border):内边距外的边框,可以使用 border 属性定义边框的样式、宽度和颜色,如 border: 1px solid #000;
    • 外边距(Margin):元素与相邻元素之间的空白区域,可以使用 margin 属性设置,如 margin: 10px;

7 请解释一下动画在前端开发中的应用及其实现方式

(1)考察点分析

  • 基本概念理解:考察候选人对动画的基本概念和在前端开发中的重要性理解。
  • 技术实现能力:考察候选人是否了解和熟悉常见的动画实现方式,如 CSS 动画和 JavaScript 动画。
  • 性能优化和交互设计:考察候选人在动画设计中如何考虑性能优化和用户交互体验。

(2)最终答案

  1. 动画概述

    动画在前端开发中广泛应用,用于增强用户体验和提升网页交互性。它能够吸引用户的注意力,改善页面流畅性,并提供反馈和状态转换的视觉效果。

  2. 实现方式

    • CSS 动画:使用 @keyframes 定义动画的关键帧,结合 transition 属性实现简单的过渡效果和动画效果。例如:

      .box {
        width: 100px;
        height: 100px;
        background-color: red;
        transition: width 0.3s ease-in-out;
      }
      
      .box:hover {
        width: 150px;
      }
      
    • JavaScript 动画:通过 JavaScript 可以实现更复杂的动画效果和交互。常用的库如 GSAP(GreenSock Animation Platform)提供了丰富的动画 API,可以实现复杂的序列动画和交互效果。

      // 使用 GSAP 库实现动画
      gsap.to(".box", { duration: 1, x: 100, rotation: 360, ease: "power2.inOut" });
      
  3. 性能优化和交互设计

    • 动画性能优化包括减少动画数量和复杂度、使用硬件加速、避免频繁的布局重绘和重排。
    • 在设计动画时,应考虑用户交互体验和无障碍访问,确保动画不会干扰用户的操作和浏览体验。

8 如何使用 Flex实现水平和垂直居中

(1)考察点分析

  • Flexbox 基础理解:考察候选人对 Flexbox 布局模型的基本理解和应用能力。
  • 居中实现能力:考察候选人是否能清晰地解释如何使用 Flexbox 实现元素的水平和垂直居中。
  • 应对不同情况的能力:考察候选人在面对各种布局需求时,如何灵活应用 Flexbox 进行布局和居中。

(2)最终答案

  1. Flexbox 概述

    Flexbox 是一种用于页面布局的弹性盒子模型,通过设置父容器的 display: flex; 属性,可以控制子项目的布局和排列方式。

  2. 水平居中实现

    .container {
      display: flex;
      justify-content: center; /* 水平居中 */
    }
    

    上述代码中,通过设置 .container 容器为 flex 布局,并使用 justify-content: center; 属性将子项目水平居中。

9 请解释一下 JavaScript 中的闭包(Closure)及其应用场景

(1)考察点分析

  • 基本概念理解:考察候选人对闭包的基本定义和理解能力。
  • 闭包的特性和作用:考察候选人是否能清晰地说明闭包的特性以及在实际开发中的作用。
  • 应用场景:考察候选人能否举例说明闭包在实际开发中的常见应用场景。

(2)最终答案

  1. 闭包概述

    闭包是指在函数内部创建另一个函数,并且内部函数可以访问外部函数的变量和参数,即使外部函数已经执行完毕。闭包通过保存了外部函数的执行环境,使得内部函数能够继续访问和操作外部函数的变量。

  2. 特性和作用

    • 保护变量:内部函数可以访问外部函数的局部变量,但外部函数无法直接访问内部函数的变量,从而实现了数据的私有化和保护。
    • 延长变量的生命周期:外部函数执行完毕后,其作用域中的变量在闭包的作用下仍然可以被内部函数访问和操作,延长了变量的生命周期。
    • 模块化开发:利用闭包可以实现模块化开发,封装私有变量和方法,并提供接口供外部调用,实现了模块的封装和重用。
  3. 应用场景示例

    • 事件处理函数:在事件监听中使用闭包可以保存事件触发时的状态或数据。

      function addClickHandler() {
        var count = 0;
        document.getElementById('btn').addEventListener('click', function() {
          count++;
          console.log('Button clicked ' + count + ' times');
        });
      }
      
    • 定时器:利用闭包可以保存定时器中的变量状态,实现循环或延时操作。

      function delayedMessage() {
        var count = 0;
        var interval = setInterval(function() {
          count++;
          console.log('Delayed message ' + count);
          if (count >= 5) {
            clearInterval(interval);
          }
        }, 1000);
      }
      

10 请解释一下 JavaScript 中的深拷贝与浅拷贝及其区别

(1)考察点分析

  • 基本概念理解:考察候选人对深拷贝和浅拷贝的基本概念和区别的理解。
  • 实际操作能力:考察候选人是否能清晰地解释和实现深拷贝和浅拷贝的方法。
  • 问题解决能力:考察候选人在面对不同的对象复制需求时,如何选择和应用合适的方法进行拷贝。

(2)最终答案

  1. 深拷贝与浅拷贝概述

    • 浅拷贝:浅拷贝是指创建一个新对象,这个新对象的属性是原对象属性的引用。浅拷贝只复制对象的第一层属性,如果属性是对象,则只复制其引用,原对象和新对象共享同一块内存空间。
    • 深拷贝:深拷贝是指创建一个新对象,并递归地复制所有嵌套对象的属性,使得新对象和原对象完全独立,互不影响。
  2. 浅拷贝的实现方法

    • 使用 Object.assign() 实现浅拷贝:

      let obj1 = { a: 1, b: { c: 2 } };
      let shallowCopy = Object.assign({}, obj1);
      
    • 使用展开运算符 ... 实现浅拷贝:

      let shallowCopy = { ...obj1 };
      
  3. 深拷贝的实现方法

    • 使用递归函数手动实现深拷贝:

      function deepClone(obj) {
        if (obj === null || typeof obj !== 'object') {
          return obj;
        }
        let copy = Array.isArray(obj) ? [] : {};
        for (let key in obj) {
          if (obj.hasOwnProperty(key)) {
            copy[key] = deepClone(obj[key]);
          }
        }
        return copy;
      }
      
    • 使用 JSON.parse(JSON.stringify()) 实现深拷贝(注意局限性):

      let deepCopy = JSON.parse(JSON.stringify(obj1));
      
    • 使用 Lodash 的 _.cloneDeep 方法实现深拷贝:

      let _ = require('lodash');
      let deepCopy = _.cloneDeep(obj1);
      
  4. 实际应用和注意事项

    • 在实际开发中,选择深拷贝还是浅拷贝取决于具体需求。如果只需要复制对象的第一层属性,浅拷贝通常更高效;如果需要完全独立的副本,深拷贝是必需的。
    • 使用 JSON.parse(JSON.stringify()) 方法时需要注意它的局限性,如不能复制函数、不可枚举属性、以及处理循环引用的能力。
    • 处理复杂对象结构或需要高性能时,可以考虑使用第三方库如 Lodash 来实现深拷贝。

11 请解释一下 ES6(ECMAScript 2015)有哪些新特性,并举例说明其应用。

(1)考察点分析

  • 基础知识掌握:考察候选人对 ES6 的基本新特性是否熟悉。
  • 理解和应用能力:考察候选人是否能够理解每个新特性及其应用场景。
  • 实际编码能力:考察候选人是否能够通过代码示例展示对新特性的理解和应用。

(2)最终答案

  1. 简要概述 ES6

    ES6 是 ECMAScript 的第六版,也是 JavaScript 的重大更新,带来了许多新特性和改进,提升了开发效率和代码可读性。

  2. 主要新特性及应用

    • letconst 声明:块级作用域变量声明。

      let x = 10;
      const y = 20;
      
    • 箭头函数:更简洁的函数语法和词法作用域的 this 绑定。

      const add = (a, b) => a + b;
      console.log(add(5, 3)); // 8
      
    • 模板字符串:嵌入变量的字符串模板,更加简洁易读。

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

      const [a, b] = [1, 2];
      const {x, y} = {x: 3, y: 4};
      console.log(a, b); // 1, 2
      console.log(x, y); // 3, 4
      
    • 默认参数:为函数参数设置默认值。

      function greet(name = 'Guest') {
        return `Hello, ${name}!`;
      }
      console.log(greet()); // Hello, Guest!
      console.log(greet('Bob')); // Hello, Bob!
      
    • 展开运算符(...:用于数组和对象的展开。

      const arr1 = [1, 2, 3];
      const arr2 = [...arr1, 4, 5];
      console.log(arr2); // [1, 2, 3, 4, 5]
      
    • for...of 循环:遍历可迭代对象的值。

      const numbers = [10, 20, 30];
      for (const num of numbers) {
        console.log(num);
      }
      
    • MapSet 数据结构:提供更灵活的数据存储方式。

      const map = new Map();
      map.set('key1', 'value1');
      console.log(map.get('key1')); // value1
      
      const set = new Set([1, 2, 3]);
      set.add(4);
      console.log(set.has(2)); // true
      
    • 模块化:使用 importexport 进行模块化开发。

      // module.js
      export const greet = (name) => `Hello, ${name}!`;
      
      // main.js
      import { greet } from './module.js';
      console.log(greet('Alice')); // Hello, Alice!
      
    • 类(Class):更接近面向对象编程的语法糖。

      class Person {
        constructor(name) {
          this.name = name;
        }
        greet() {
          return `Hello, ${this.name}!`;
        }
      }
      const alice = new Person('Alice');
      console.log(alice.greet()); // Hello, Alice!
      
    • Promise:异步编程的新方式,取代回调函数。

      const fetchData = () => {
        return new Promise((resolve, reject) => {
          setTimeout(() => resolve('Data fetched'), 1000);
        });
      };
      fetchData().then((data) => console.log(data)); // Data fetched
      

12 请解释一下 JavaScript 中的箭头函数及其应用场景

(1)考察点分析

  • 基本概念理解:考察候选人对箭头函数基本语法和特点的理解。
  • 特性和使用场景:考察候选人是否能够清晰地说明箭头函数的特性及其在实际开发中的应用场景。
  • 比较和选择能力:考察候选人能否理解箭头函数与传统函数的区别,并在适当的场景中做出选择。

(2)最终答案

  1. 箭头函数概述

    箭头函数是一种更简洁的函数书写方式,引入了新的语法,减少了冗余代码。箭头函数不会创建自己的 this,而是从外层作用域继承 this

  2. 箭头函数的语法

    • 基本语法:使用箭头 => 定义函数。
    • 单个参数时,可以省略参数括号:
      const add = x => x + 10;
      console.log(add(5)); // 15
      
    • 多个参数时,需要使用括号:
      const add = (x, y) => x + y;
      console.log(add(5, 3)); // 8
      
    • 单行语句隐式返回结果:
      const square = x => x * x;
      console.log(square(4)); // 16
      
    • 多行语句需使用花括号和 return 语句:
      const sum = (x, y) => {
        const result = x + y;
        return result;
      };
      console.log(sum(5, 3)); // 8
      
  3. 箭头函数的特性

    • 没有 this 绑定:箭头函数不会创建自己的 this,而是继承自外层作用域:
      function Timer() {
        this.seconds = 0;
        setInterval(() => {
          this.seconds++;
          console.log(this.seconds);
        }, 1000);
      }
      const timer = new Timer(); // 每秒输出递增的数字
      
    • 没有 arguments 对象:箭头函数没有自己的 arguments 对象,可使用展开运算符 ...args 代替:
      const sum = (...args) => args.reduce((acc, val) => acc + val, 0);
      console.log(sum(1, 2, 3, 4)); // 10
      
    • 不能用作构造函数:箭头函数不能使用 new 关键字实例化对象。
    • 没有 prototype 属性:箭头函数没有 prototype 属性。
  4. 应用场景

    • 简化回调函数:箭头函数在回调函数中使用简洁明了:
      const numbers = [1, 2, 3, 4];
      const doubled = numbers.map(n => n * 2);
      console.log(doubled); // [2, 4, 6, 8]
      
    • 保持 this 上下文:在需要保持当前 this 上下文的场景中使用,如事件处理器、定时器等:
      class MyComponent {
        constructor() {
          this.name = 'MyComponent';
          document.getElementById('myButton').addEventListener('click', () => {
            console.log(this.name); // 输出 'MyComponent'
          });
        }
      }
      const component = new MyComponent();
      

13 请解释一下 JavaScript 中 Map 和 Object 的区别

(1)考察点分析

  • 基础知识理解:考察候选人对 Map 和 Object 基本概念的理解。
  • 特性和使用场景:考察候选人是否能清晰地说明 Map 和 Object 各自的特性及其在实际开发中的适用场景。
  • 实际操作能力:考察候选人是否能够通过代码示例展示对 Map 和 Object 的理解和应用。

(2)最终答案

  1. 基本概念

    • Object:用于存储键值对,键名只能是字符串或符号(Symbol)。
    • Map:一种新的键值对存储结构,键名可以是任意类型的值(包括对象)。
  2. 主要区别

    • 键的类型

      • Object 的键名只能是字符串或符号。
      • Map 的键名可以是任意类型。
    • 键值对的迭代顺序

      • Object 的键值对没有固定的迭代顺序。
      • Map 的键值对按照插入顺序进行迭代。
    • 性能

      • Map 在频繁增删键值对时性能较优。
      • Object 在简单的、静态键值对存储时较为高效。
    • 常用操作

      • Map 提供了更丰富的操作方法,如 setgethasdeleteclear
      • Object 需要使用更多的原生方法来实现类似功能。
    • 大小

      • Mapsize 属性,可以直接获取键值对的数量。
      • Object 没有直接获取大小的属性,需要手动计算。
    • 原型链

      • Object 有原型链,会继承一些默认属性和方法。
      • Map 不会继承任何默认属性和方法。
  3. 代码示例

    • 创建和操作 Map

      const map = new Map();
      map.set('key1', 'value1');
      map.set(2, 'value2');
      console.log(map.get('key1')); // 'value1'
      console.log(map.get(2)); // 'value2'
      console.log(map.size); // 2
      map.delete('key1');
      console.log(map.has('key1')); // false
      
    • 创建和操作 Object

      const obj = {};
      obj['key1'] = 'value1';
      obj[2] = 'value2';
      console.log(obj['key1']); // 'value1'
      console.log(obj[2]); // 'value2'
      console.log(Object.keys(obj).length); // 2
      delete obj['key1'];
      console.log('key1' in obj); // false
      

14 请解释一下 MVVM 和 MVC 的区别

(1)考察点分析

  • 基础概念理解:考察候选人对 MVVM 和 MVC 两种架构模式的基本理解。
  • 特性和结构:考察候选人是否能清晰地说明两者的结构及其特性。
  • 应用场景和选择能力:考察候选人能否理解 MVVM 和 MVC 的适用场景,并在实际开发中做出选择。

(2)最终答案

  1. MVVM 和 MVC 概述

    • MVC(Model-View-Controller):一种经典的软件架构模式,将应用分为三个部分:模型(Model)、视图(View)和控制器(Controller)。
    • MVVM(Model-View-ViewModel):一种现代的软件架构模式,将应用分为三个部分:模型(Model)、视图(View)和视图模型(ViewModel)。
  2. 结构和特性

    • MVC

      • Model:负责数据和业务逻辑。
      • View:负责用户界面展示。
      • Controller:处理用户输入,更新模型和视图。
    • MVVM

      • Model:负责数据和业务逻辑。
      • View:负责用户界面展示。
      • ViewModel:处理视图的行为和状态,通过双向数据绑定将视图和模型连接起来。
  3. 主要区别

    • 数据绑定

      • MVC:数据绑定通常是手动的,通过控制器来更新视图。
      • MVVM:采用双向数据绑定,视图和模型之间的同步由框架自动处理。
    • 关注点分离

      • MVC:控制器直接处理用户输入,关注点相对集中。
      • MVVM:视图模型解耦了视图和模型,使得视图和业务逻辑的关注点更加清晰。
    • 复杂度和灵活性

      • MVC:结构较为简单,适用于中小型应用。
      • MVVM:适用于需要复杂状态管理和动态更新的大型应用。
    • 框架支持

      • MVC:常见于服务器端框架,如 Ruby on Rails、ASP.NET MVC。
      • MVVM:常见于前端框架,如 Angular、Vue.js。
  4. 代码示例

    • MVC

      // Model
      class Model {
        constructor() {
          this.data = '';
        }
        setData(newData) {
          this.data = newData;
        }
        getData() {
          return this.data;
        }
      }
      
      // View
      class View {
        constructor(controller) {
          this.controller = controller;
          this.init();
        }
        init() {
          document.getElementById('button').addEventListener('click', () => {
            this.controller.updateModel();
          });
        }
        render(data) {
          document.getElementById('output').innerText = data;
        }
      }
      
      // Controller
      class Controller {
        constructor(model, view) {
          this.model = model;
          this.view = view;
        }
        updateModel() {
          this.model.setData('Hello MVC');
          this.view.render(this.model.getData());
        }
      }
      
      const model = new Model();
      const controller = new Controller(model);
      const view = new View(controller);
      
    • MVVM(以 Vue.js 为例):

      
      <div id="app">
        <input v-model="message" />
        <p>{{ message }}p>
      div>
      
      <script>
      // ViewModel
      new Vue({
        el: '#app',
        data: {
          message: 'Hello MVVM'
        }
      });
      script>
      

15 请解释一下 Vue 的生命周期

(1)考察点分析

  • 基础概念理解:考察候选人对 Vue 生命周期的基本理解。
  • 各个生命周期钩子函数的作用和用法:考察候选人是否能清晰地说明各个生命周期钩子的作用和具体使用场景。
  • 实际操作能力:考察候选人是否能够通过代码示例展示对生命周期钩子函数的理解和应用。

(2)最终答案

  1. Vue 生命周期概述

    Vue 实例在创建过程中会经历一系列的初始化过程,从创建、挂载、更新到销毁,这一过程称为生命周期。

  2. 各个生命周期钩子函数的介绍

    • beforeCreate:实例初始化之后,数据观测和事件配置之前调用。
    • created:实例创建完成,数据观测和事件配置已完成,但还未挂载到 DOM。
    • beforeMount:在挂载开始之前调用,相关的 render 函数首次被调用。
    • mounted:实例挂载到 DOM 后调用,DOM 操作可以在此进行。
    • beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
    • updated:由于数据更改导致虚拟 DOM 重新渲染和打补丁后调用。
    • beforeDestroy:实例销毁之前调用,此时实例仍然完全可用。
    • destroyed:实例销毁后调用,组件的所有事件监听器被移除,所有子实例也被销毁。
  3. 代码示例

    展示一个简单的 Vue 组件,使用所有生命周期钩子函数:

    <template>
      <div>
        <p>{{ message }}</p>
        <button @click="updateMessage">Update Message</button>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          message: 'Hello Vue!'
        };
      },
      beforeCreate() {
        console.log('beforeCreate: 实例初始化之后,数据观测和事件配置之前调用');
      },
      created() {
        console.log('created: 实例创建完成,数据观测和事件配置已完成,但还未挂载到 DOM');
      },
      beforeMount() {
        console.log('beforeMount: 在挂载开始之前调用,相关的 render 函数首次被调用');
      },
      mounted() {
        console.log('mounted: 实例挂载到 DOM 后调用,DOM 操作可以在此进行');
      },
      beforeUpdate() {
        console.log('beforeUpdate: 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前');
      },
      updated() {
        console.log('updated: 由于数据更改导致虚拟 DOM 重新渲染和打补丁后调用');
      },
      beforeDestroy() {
        console.log('beforeDestroy: 实例销毁之前调用,此时实例仍然完全可用');
      },
      destroyed() {
        console.log('destroyed: 实例销毁后调用,组件的所有事件监听器被移除,所有子实例也被销毁');
      },
      methods: {
        updateMessage() {
          this.message = 'Message Updated!';
        }
      }
    };
    </script>
    

16 请解释一下 Vue 父子组件的生命周期顺序

(1)考察点分析

  • 生命周期钩子的理解:考察候选人对 Vue 组件生命周期钩子的理解。
  • 父子组件的生命周期顺序:考察候选人是否清楚父子组件在创建和销毁时生命周期钩子的触发顺序。
  • 实际操作能力:考察候选人是否能够通过代码示例展示对父子组件生命周期顺序的理解和应用。

(2)最终答案

  1. 父子组件生命周期钩子的基本概念

    了解 Vue 组件的生命周期钩子:beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestroyed

  2. 父子组件生命周期的触发顺序

    • 创建阶段

      1. 父组件的 beforeCreate
      2. 父组件的 created
      3. 父组件的 beforeMount
      4. 子组件的 beforeCreate
      5. 子组件的 created
      6. 子组件的 beforeMount
      7. 子组件的 mounted
      8. 父组件的 mounted
    • 更新阶段

      1. 父组件的 beforeUpdate
      2. 子组件的 beforeUpdate
      3. 子组件的 updated
      4. 父组件的 updated
    • 销毁阶段

      1. 父组件的 beforeDestroy
      2. 子组件的 beforeDestroy
      3. 子组件的 destroyed
      4. 父组件的 destroyed
  3. 代码示例

    展示一个简单的父子组件,打印出生命周期钩子的触发顺序:

    父组件

   
   

子组件:





17 Vue双向数据绑定的原理

(1)考察点分析

  • 理解数据绑定的机制:考察候选人对 Vue 双向数据绑定的底层实现原理的理解。
  • 数据劫持与发布订阅模式:考察候选人是否了解 Vue 是如何利用数据劫持和发布订阅模式来实现双向数据绑定的。
  • Vue 的响应式系统:考察候选人对 Vue 响应式系统的基本了解,包括依赖追踪和更新策略。

(2)最终答案

  1. 双向数据绑定的基本概念

    Vue 的双向数据绑定是指数据模型层和视图层之间的自动同步,数据的变化会实时更新到视图,而视图中的变化也会反映到数据模型中。

  2. 数据劫持(Object.defineProperty)

    Vue 使用 Object.defineProperty 方法来劫持对象属性的访问和修改。通过这种方式,Vue 能够监听到每个属性的变化,从而实现数据的响应式更新。

  3. 发布订阅模式

    在 Vue 中,每个数据都有对应的依赖收集器(Dep),而视图中的指令(比如 v-model)会创建一个监听器(Watcher)。当数据变化时,会触发对应属性的依赖收集器,通知所有相关的监听器更新视图。

  4. 响应式系统的运作机制

    • 初始化阶段
      • Vue 在组件实例化时,对数据对象进行递归遍历,对每个属性使用 Object.defineProperty 进行数据劫持。
      • 同时,为每个属性初始化一个依赖收集器(Dep),用来存储依赖该属性的监听器(Watcher)。
    • 依赖追踪
      • 当模板中使用了某个属性时,会创建一个对应的监听器(Watcher),并将其加入到属性的依赖收集器中。
      • 当属性被修改时,会触发该属性的依赖收集器,通知所有相关的监听器更新视图。
    • 更新策略
      • Vue 使用虚拟 DOM 和差异化比较算法,找出需要更新的部分,并高效地更新到视图上,从而实现了快速响应和高效的渲染性能。

18 vue组件的通信方式

(1)考察点分析

  • 组件通信方式:考察候选人是否了解 Vue 中组件之间通信的多种方式,如 props、事件、Vuex 等。
  • 适用场景和优缺点:考察候选人是否能根据不同的场景选择合适的通信方式,并理解各种方式的优缺点。
  • 实际应用能力:考察候选人是否能够通过示例或场景来展示组件通信的实际应用能力。

(2)最终答案

  1. 组件通信的基本方式

    • props / $emit:父子组件通信的基础方式。父组件通过 props 向子组件传递数据,子组件通过 $emit 触发事件向父组件传递数据。

    • 事件总线:利用 Vue 实例作为中央事件总线,可以在任意关系的组件之间传递事件或数据,适合于兄弟组件通信。

    • Vuex:用于大型应用中集中管理共享状态。通过 store 存储和管理状态,各组件通过 getters 和 mutations 修改和获取状态。

    • $attrs / $listeners:透传父组件的属性和监听器到子组件,简化了属性的传递和事件的监听,适合于需要传递大量属性的情况。

    • provide / inject:祖先组件通过 provide 提供数据,后代组件通过 inject 注入数据,适合于跨层级组件通信的场景。

  2. 场景和优缺点分析

    • props / $emit:简单直观,适合于父子组件通信,但对于复杂嵌套或多层级组件通信可能会显得繁琐。

    • 事件总线:适合于非直接关系的组件通信,但组件之间的关系不明确,不易于追踪和调试。

    • Vuex:适合于状态复杂或需要多个组件共享状态的场景,但对于小型应用可能会显得过于复杂。

    • $attrs / $listeners:适合于需要将父组件的属性和事件传递给子组件的情况,但不适合于多层级嵌套的场景。

    • provide / inject:适合于祖先组件向所有后代组件传递依赖,但当跨级关系不清晰时会导致组件之间的耦合性增加。

  3. 示例代码

    父子组件通信示例(props / $emit)

    ParentComponent.vue

   

   

ChildComponent.vue:




这个示例展示了父组件通过 props 向子组件传递数据,并通过 $emit 触发事件向父组件传递数据的过程。

19 如何解决跨域问题?

(1)考察点分析

  • 理解跨域原理:考察候选人对跨域问题的基本理解,包括同源策略及其限制。
  • 解决跨域的方法:考察候选人是否了解常见的跨域解决方案,如 CORS、JSONP、代理服务器等。
  • 安全性考虑:考察候选人在解决跨域问题时是否考虑到安全性和最佳实践。

(2)最终答案

  1. 同源策略

    浏览器的同源策略限制了从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。

  2. 解决跨域的常见方法

    • CORS(跨域资源共享):通过服务端设置响应头部,如 Access-Control-Allow-Origin,允许指定的源访问资源。

    • JSONP(JSON with Padding):利用 # 使用 Lozad.js: Lazy Loaded Image

      1. 性能优化
        图片懒加载可以显著减少初始页面的加载时间,特别是在图片较多的情况下。通过减少不必要的图片加载,懒加载降低了服务器带宽消耗,优化了用户体验,尤其是对长列表或移动设备上的用户体验改善显著。

      综上所述,图片懒加载是通过延迟加载图片来优化页面性能的一种技术,可以使用原生 JavaScript 或第三方库实现。

      39 说一下原型和原型链

      (1)考察点分析

      • 基础概念:了解 JavaScript 中的原型和原型链的基本概念。
      • 作用与机制:理解原型和原型链在对象继承中的作用与机制。
      • 实际应用:掌握如何在实际开发中使用和调试原型链。

      (2)最终答案

      在 JavaScript 中,原型和原型链是理解对象继承和属性查找机制的关键概念。以下是对原型和原型链的详细解释。

      1. 基本概念

        • 原型
          每个 JavaScript 对象都有一个内部属性 [[Prototype]],指向另一个对象,这个对象被称为原型。原型对象可以包含该对象共享的属性和方法。
          在现代 JavaScript 中,可以通过 __proto__ 访问原型(不推荐),或者使用 Object.getPrototypeOfObject.setPrototypeOf 方法。

          示例:

          function Person(name) {
            this.name = name;
          }
          
          Person.prototype.greet = function() {
            console.log(`Hello, my name is ${this.name}`);
          };
          
          const alice = new Person('Alice');
          alice.greet(); // 输出:Hello, my name is Alice
          
        • 原型链
          原型链是由对象及其原型组成的链式结构。当访问一个对象的属性时,如果该属性不存在于对象本身,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或到达原型链的末端。

          示例:

          console.log(alice.__proto__ === Person.prototype); // true
          console.log(Person.prototype.__proto__ === Object.prototype); // true
          console.log(Object.prototype.__proto__ === null); // true
          
      2. 作用与机制

        • 属性查找
          当访问一个对象的属性时,JavaScript 会先在对象自身的属性中查找。如果没有找到,则会沿着原型链向上查找,直到找到属性或到达原型链的末端(即 null)。

          示例:

          console.log(alice.hasOwnProperty('name')); // true
          console.log(alice.hasOwnProperty('greet')); // false, greet 在原型链上
          console.log('greet' in alice); // true, 因为 greet 在原型链上
          
        • 继承机制
          通过原型链,可以实现对象的继承,使得一个对象可以共享另一个对象的属性和方法。构造函数和原型是常见的实现方式。

          示例:

          function Employee(name, jobTitle) {
            Person.call(this, name); // 调用父构造函数
            this.jobTitle = jobTitle;
          }
          
          Employee.prototype = Object.create(Person.prototype);
          Employee.prototype.constructor = Employee;
          
          Employee.prototype.work = function() {
            console.log(`${this.name} is working as ${this.jobTitle}`);
          };
          
          const bob = new Employee('Bob', 'Engineer');
          bob.greet(); // 输出:Hello, my name is Bob
          bob.work(); // 输出:Bob is working as Engineer
          
      3. 实际应用

        • 使用构造函数和原型实现对象的继承,如上例所示。

        • 通过 Object.create 创建具有特定原型的对象:

          const personPrototype = {
            greet() {
              console.log(`Hello, my name is ${this.name}`);
            }
          };
          
          const charlie = Object.create(personPrototype);
          charlie.name = 'Charlie';
          charlie.greet(); // 输出:Hello, my name is Charlie
          
        • 理解原型链有助于调试和优化代码。例如,避免在对象本身和原型链上存在重名的属性,以减少不必要的查找开销。

      综上所述,原型和原型链是 JavaScript 中对象继承和属性查找的基础。理解这些概念可以帮助你更好地掌握 JavaScript 的对象机制和编程技巧。

      40 类和对象的区别

      (1)考察点分析

      • 基础概念:了解类和对象的定义及其在编程中的作用。
      • 实例化:理解类是如何被实例化为对象的。
      • 应用场景:掌握类和对象在实际编程中的使用场景和区别。

      (2)最终答案

      1. 基本概念


        • 类是一个模板或蓝图,用于定义对象的属性和方法。在类中,可以定义属性(成员变量)和方法(成员函数),这些属性和方法描述了对象的状态和行为。

          示例:

          class Person {
            constructor(name, age) {
              this.name = name;
              this.age = age;
            }
          
            greet() {
              console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
            }
          }
          
        • 对象
          对象是类的实例。对象包含类定义的实际数据和行为(方法)。每个对象都有自己独立的属性,但它们共享类定义的结构和行为。

          示例:

          const alice = new Person('Alice', 30);
          alice.greet(); // 输出:Hello, my name is Alice and I am 30 years old.
          
          const bob = new Person('Bob', 25);
          bob.greet(); // 输出:Hello, my name is Bob and I am 25 years old.
          
      2. 实例化

        • 类通过实例化来创建对象。实例化是创建类的实例(对象)的过程。在上面的示例中,通过 new Person('Alice', 30)new Person('Bob', 25) 实例化了两个 Person 对象。
      3. 应用场景


        • 类用于定义和创建具有相同属性和行为的多个对象。例如,定义一类学生,每个学生都有姓名、年龄、学号等属性和方法。

          示例:

          class Student {
            constructor(name, id) {
              this.name = name;
              this.id = id;
            }
          
            study() {
              console.log(`${this.name} is studying.`);
            }
          }
          
        • 对象
          对象是实际使用类创建的实例,用于存储和操作数据。例如,创建具体的学生对象来表示具体的学生。

          示例:

          const student1 = new Student('John', 'S001');
          const student2 = new Student('Jane', 'S002');
          
          student1.study(); // 输出:John is studying.
          student2.study(); // 输出:Jane is studying.
          

      综上所述,类是用于定义对象的模板或蓝图,而对象是类的具体实例。类定义了对象的属性和方法,而对象则包含实际的数据和行为。通过类的实例化,可以创建多个具有相同结构和行为的对象,从而实现面向对象编程的抽象和复用。

      41 说一下浏览器的事件循环

      (1)考察点分析

      1. 基础概念:了解浏览器事件循环的基本原理。
      2. 执行顺序:理解同步任务和异步任务的执行顺序。
      3. 实际应用:掌握事件循环在实际编程中的应用和调试。

      (2)最终答案

      1. 基本概念
      • 事件循环
        事件循环是浏览器和 Node.js 处理异步操作的核心机制。它是一个不断检查和执行任务的循环过程,确保异步操作按正确顺序执行。
      • 调用栈
        调用栈是一个栈数据结构,用于存储代码执行的上下文。当函数调用时,函数的执行上下文被压入调用栈,当函数执行完毕时,执行上下文被弹出调用栈。
      • 任务队列
        任务队列用于存储待执行的异步任务。当调用栈为空时,事件循环从任务队列中取出任务并执行。
      1. 执行顺序
      • 同步任务
        同步任务在调用栈中按顺序执行,执行完毕后调用栈清空。

      • 异步任务
        异步任务分为宏任务(macro task)和微任务(micro task)。宏任务包括 setTimeoutsetInterval、I/O 操作等。微任务包括 Promisethencatchfinally 回调和 MutationObserver

      • 事件循环执行过程

        1. 执行所有同步任务,直到调用栈为空。
        2. 执行所有微任务(如果有),例如 Promise 的回调。
        3. 执行一个宏任务,例如 setTimeout 回调。
        4. 重复上述过程。

        示例:

        console.log('Start');
        
        setTimeout(() => {
          console.log('setTimeout');
        }, 0);
        
        Promise.resolve().then(() => {
          console.log('Promise');
        });
        
        console.log('End');
        

      解析:
      1.console.log(‘Start’) 是同步任务,立即执行。
      2.setTimeout 是宏任务,回调被放入宏任务队列。
      3.Promise.resolve().then 是微任务,回调被放入微任务队列。
      4.console.log(‘End’) 是同步任务,立即执行。
      5.调用栈为空,执行微任务队列中的回调,输出 Promise。
      6.执行宏任务队列中的回调,输出 setTimeout。
      输出顺序为:

      Start
      End
      Promise
      setTimeout
      
      
      1. 实际应用
      • 使用 setTimeout 和 Promise 进行异步编程:
        使用 setTimeout 来延迟执行代码,使用 Promise 来处理异步操作并链式调用。
      • 理解和调试异步代码的执行顺序:
        理解事件循环的执行顺序,有助于避免常见的异步编程问题,例如回调地狱和竞态条件。
        示例:
      function asyncOperation() {
        return new Promise((resolve) => {
          setTimeout(() => {
            resolve('Operation Complete');
          }, 1000);
        });
      }
      
      asyncOperation().then((message) => {
        console.log(message); // 输出:Operation Complete
      });
      
      console.log('Async operation started');
      
      

      解析:
      1、asyncOperation 返回一个 Promise,其回调被放入宏任务队列。
      2、console.log(‘Async operation started’) 是同步任务,立即执行。
      3、1 秒后,宏任务队列中的回调执行,输出 Operation Complete。
      输出顺序为:

      Async operation started
      Operation Complete
      

      综上所述,浏览器的事件循环通过调用栈和任务队列管理同步和异步任务的执行顺序,确保代码按照预期顺序运行。理解事件循环有助于编写和调试异步代码,提高代码的执行效率和可维护性。

      42 cookie的作用是什么?

      (1)考察点分析

      • 理解 Cookie 的基本概念:考察候选人是否理解 Cookie 的定义和基本用途。
      • 了解 Cookie 的作用:考察候选人是否能具体解释 Cookie 在 Web 应用中的具体作用和应用场景。
      • 安全性和限制:考察候选人是否了解 Cookie 的安全性问题及其使用中的限制。

      (2)最终答案

      1. Cookie 的基本概念

        • Cookie 是存储在用户浏览器中的小型文本文件。
        • 由服务器发送并由客户端存储,每次客户端请求时发送回服务器。
      2. Cookie 的作用

        • 状态管理:跟踪和存储用户会话状态,如登录状态、购物车内容等。
        • 个性化设置:存储用户的偏好设置,如语言选择、主题设置等。
        • 跟踪和分析:用于网站分析和广告跟踪,帮助网站了解用户行为和优化体验。
      3. 安全性和限制

        • 安全性:Cookie 存在安全风险,如被窃取、劫持等。通过 HttpOnlySecureSameSite 属性可以提高安全性。
        • 大小限制:单个 Cookie 的大小通常不能超过 4KB,一个域名下的 Cookie 总数也有限制(一般为 20 个)。
        • 生命周期:Cookie 可以设置过期时间或在浏览器会话结束时删除。
      示例代码
      // 设置 Cookie
      document.cookie = "username=JohnDoe; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/; Secure; HttpOnly; SameSite=Strict";
      
      // 读取 Cookie
      const getCookie = (name) => {
        const value = `; ${document.cookie}`;
        const parts = value.split(`; ${name}=`);
        if (parts.length === 2) return parts.pop().split(';').shift();
      };
      
      console.log(getCookie("username")); // 输出: JohnDoe
      

      43 cookie的作用是什么?

      (1)考察点分析

      • 理解 Cookie 过期时间的设置机制:考察候选人是否了解如何设置 Cookie 的过期时间及其背后的原理。
      • 理解 Cookie 的携带机制:考察候选人是否了解 Cookie 在客户端和服务器之间的传递过程。

      (2)最终答案

      1. Cookie 过期时间的设置

        • 由服务器设置:服务器在发送 Set-Cookie 头时,可以指定 Cookie 的过期时间。
        • 由客户端设置:客户端也可以通过 JavaScript 设置 Cookie 的过期时间。
      2. Cookie 的携带机制

        • 由浏览器自动携带:浏览器会在发送请求时自动携带符合条件的 Cookie。
        • 条件:只有与请求的域名和路径匹配的 Cookie 才会被携带,并且要考虑安全属性(如 Secure 和 HttpOnly)。
      3. 示例代码

      设置 Cookie 过期时间

      服务器端设置:

      HTTP/1.1 200 OK
      Set-Cookie: username=JohnDoe; Expires=Wed, 21 Oct 2024 07:28:00 GMT; Path=/;
      

      客户端 JavaScript 设置:

      document.cookie = "username=JohnDoe; expires=Wed, 21 Oct 2024 07:28:00 GMT; path=/;";
      

      浏览器自动携带 Cookie
      当用户访问匹配域名和路径的页面时,浏览器会自动携带相关的 Cookie:

      GET /path/to/resource HTTP/1.1
      Host: www.example.com
      Cookie: username=JohnDoe
      

      总结
      Cookie 过期时间的设置:可以由服务器在 Set-Cookie 头中设置,也可以由客户端通过 JavaScript 设置。
      Cookie 的携带:由浏览器在请求时自动完成,浏览器会根据请求的域名和路径,自动携带符合条件的 Cookie。

      44 undefine和null的区别

      (1)考察点分析

      • 理解基本概念:考察候选人是否清楚 undefined 和 null 各自的定义和用途。
      • 了解使用场景:考察候选人是否能正确区分和使用 undefined 和 null。
      • 掌握类型和相等性判断:考察候选人是否了解两者在类型判断和相等性判断中的区别。

      (2)最终答案

      1. 基本概念

        • undefined:表示变量声明了但未赋值,或对象属性不存在。
        • null:表示一个变量被明确赋值为空,没有对象值。
      2. 使用场景

        • undefined:通常由 JavaScript 引擎自动分配,表示缺少值。
          • 变量声明但未赋值。
          • 对象属性不存在。
          • 函数没有返回值。
        • null:通常由开发者手动赋值,表示没有对象。
          • 明确表示变量没有对象值。
          • 重置变量或对象属性。
      3. 类型和相等性判断

        • 类型
          • typeof undefined 返回 "undefined"
          • typeof null 返回 "object"
        • 相等性判断
          • undefined == null 返回 true(抽象相等)。
          • undefined === null 返回 false(严格相等)。
      4. 示例代码

      // undefined 示例
      let a;
      console.log(a); // 输出: undefined
      
      let obj = {};
      console.log(obj.prop); // 输出: undefined
      
      function foo() {}
      console.log(foo()); // 输出: undefined
      
      // null 示例
      let b = null;
      console.log(b); // 输出: null
      
      let obj2 = { prop: null };
      console.log(obj2.prop); // 输出: null
      
      // 类型判断
      console.log(typeof undefined); // 输出: "undefined"
      console.log(typeof null); // 输出: "object"
      
      // 相等性判断
      console.log(undefined == null); // 输出: true
      console.log(undefined === null); // 输出: false
      

      45 watch和computed的区别

      (1)考察点分析

      • 响应式数据管理:考察候选人对Vue.js中响应式数据的处理方式。
      • 实时性要求:考察候选人对数据变化的实时监控和处理能力。
      • 计算属性与副作用:考察候选人对计算属性和副作用函数的理解和区别。

      (2)最终答案

      1. watch的特点和使用场景

        • 特点:可以监视数据的变化,当数据发生变化时执行自定义的函数。
        • 使用场景
          • 监听单个数据的变化,执行异步操作或复杂逻辑。
          • 监听对象或数组的变化,执行深层次的操作。
          • 监听路由变化,执行页面跳转前的处理。
      2. computed的特点和使用场景

        • 特点:基于响应式依赖进行缓存,只有依赖发生改变才会重新计算。
        • 使用场景
          • 计算和返回一个新的衍生数据,例如根据原始数据计算过滤后的列表。
          • 依赖多个响应式数据,只有当这些数据发生变化时才重新计算。
          • 在模板中使用,自动追踪依赖,确保最小化计算次数。
      3. 区别

        • 触发时机
          • watch在数据变化时立即触发相应的处理函数。
          • computed会根据其依赖的数据是否发生变化来决定是否重新计算。
        • 返回值
          • watch不关心返回值,可以执行任何副作用操作。
          • computed必须有返回值,并且返回值会被缓存,多次访问时直接返回缓存的结果。
      4. 区别

      // watch 的使用示例
      watch: {
        // 监听单个数据变化
        counter(newValue, oldValue) {
          console.log(`Counter changed from ${oldValue} to ${newValue}`);
        },
        // 监听对象深层次数据变化
        'user.name': {
          handler(newValue, oldValue) {
            console.log(`User name changed from ${oldValue} to ${newValue}`);
          },
          deep: true
        }
      }
      
      // computed 的使用示例
      computed: {
        // 根据原始数据计算过滤后的列表
        filteredList() {
          return this.list.filter(item => item.active);
        },
        // 计算和返回一个新的衍生数据
        fullName() {
          return `${this.firstName} ${this.lastName}`;
        }
      }
      

      46 节流防抖的概念和应用场景

      (1)考察点分析

      • 基础概念:了解节流(Throttle)和防抖(Debounce)的基本原理和区别。
      • 应用场景:理解节流和防抖在实际开发中的应用场景。
      • 实现方式:掌握如何在代码中实现节流和防抖。

      (2)最终答案

      1. 基本概念

        • 节流(Throttle)
          节流是在一定时间内,只允许一个事件处理函数被调用。例如,如果设置的时间间隔是 100ms,则在这 100ms 内,无论事件触发多少次,事件处理函数只会执行一次。

          实现代码:

          function throttle(func, limit) {
            let inThrottle;
            return function() {
              const context = this;
              const args = arguments;
              if (!inThrottle) {
                func.apply(context, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
              }
            }
          }
          
        • 防抖(Debounce)
          防抖是在一定时间内,如果事件被再次触发,则重新计时。只有当事件在设定的时间内没有再次触发,事件处理函数才会被调用。例如,如果设置的时间间隔是 300ms,则只有当事件触发后 300ms 内没有再次触发,事件处理函数才会执行。

          实现代码:

          function debounce(func, delay) {
            let timeout;
            return function() {
              const context = this;
              const args = arguments;
              clearTimeout(timeout);
              timeout = setTimeout(() => func.apply(context, args), delay);
            }
          }
          
      2. 应用场景

        • 节流

          1. 窗口滚动
            在用户滚动窗口时,频繁触发滚动事件会导致性能问题。使用节流技术可以限制滚动事件的触发频率,减少性能开销。

            示例:

            window.addEventListener('scroll', throttle(() => {
              console.log('Scrolled!');
            }, 100));
            
          2. 按钮点击
            防止用户快速多次点击按钮,导致多次请求发送。

            示例:

            const button = document.getElementById('myButton');
            button.addEventListener('click', throttle(() => {
              console.log('Button clicked!');
            }, 2000));
            
          3. 鼠标移动
            在拖拽或绘制操作中,控制鼠标移动事件的触发频率。

            示例:

            document.addEventListener('mousemove', throttle((event) => {
              console.log(`Mouse position: ${event.clientX}, ${event.clientY}`);
            }, 50));
            
        • 防抖

          1. 输入框搜索
            在用户输入时,防止每次输入都触发搜索请求。只有用户停止输入一段时间后,才发送搜索请求。

            示例:

            const searchInput = document.getElementById('search');
            searchInput.addEventListener('input', debounce(() => {
              console.log('Search:', searchInput.value);
            }, 300));
            
          2. 窗口调整
            在用户调整窗口大小时,频繁触发调整事件会导致性能问题。使用防抖技术可以确保调整事件在用户停止调整窗口一段时间后才触发。

            示例:

            window.addEventListener('resize', debounce(() => {
              console.log('Window resized!');
            }, 500));
            
          3. 表单验证
            在表单字段输入时,防止频繁触发验证请求。只有用户停止输入一段时间后,才进行表单验证。

            示例:

            const formInput = document.getElementById('formInput');
            formInput.addEventListener('input', debounce(() => {
              console.log('Validating form input:', formInput.value);
            }, 300));
            

      综上所述,节流和防抖是控制事件触发频率的有效方法。节流用于在一定时间内限制事件处理函数的执行次数,而防抖用于在事件频繁触发时,确保在指定时间内只执行一次事件处理函数。理解和合理使用这两种技术,可以显著提升应用的性能和用户体验。

      47 Vite 为什么这么快?在开发环境和生产环境有什么优化?

      (1)考察点分析

      • 基本概念:了解 Vite 的基本原理和工作机制。
      • 性能优化:理解 Vite 在开发环境和生产环境的优化策略。
      • 实际应用:掌握如何利用 Vite 的优势提升开发效率和构建性能。

      (2)最终答案

      1. Vite 为什么这么快

        • 即时热更新(HMR)
          Vite 利用浏览器的原生 ES 模块支持,实现了快速的模块热更新(HMR)。当源代码发生变化时,Vite 只需要更新变化的模块,而无需重新打包整个应用,大幅减少了重新编译的时间。

        • 按需编译
          在传统的构建工具中,每次修改代码后都需要重新打包整个项目。而 Vite 在开发环境中采用按需编译的方式,只有当模块被请求时才进行编译,这减少了项目启动时间和重新编译的时间。

        • 依赖预构建
          Vite 在启动时会使用 esbuild 对第三方依赖进行预构建,这使得这些依赖可以快速加载和执行。esbuild 是一个高速构建工具,相比传统的 JavaScript 打包工具,构建速度非常快。

      2. 开发环境的优化

        • 即时热更新(HMR)
          Vite 实现了高效的模块热更新,修改代码后可以立即看到变化,无需刷新整个页面。

        • 按需编译
          Vite 在开发环境中不需要预先打包整个项目,只有在模块被请求时才进行编译,从而减少启动时间和重新编译的时间。

        • 依赖预构建
          Vite 使用 esbuild 对第三方依赖进行预构建,提高了依赖的加载速度。

      3. 生产环境的优化

        • 静态资源处理
          在生产环境中,Vite 会对静态资源进行处理和优化,包括压缩、代码分割和缓存等,从而生成高性能的构建结果。

        • 基于 Rollup 的构建
          Vite 在生产环境中使用 Rollup 进行构建。Rollup 是一个高效的 JavaScript 模块打包工具,具有强大的代码优化和打包能力。通过 Rollup 的 Tree Shaking 特性,Vite 可以去除未使用的代码,减少打包后的文件体积。

        • Tree Shaking
          Vite 利用 Rollup 的 Tree Shaking 特性,自动去除未使用的代码,从而减少打包后的文件体积,提升加载速度和运行效率。

      4. 实际应用

        • 在开发环境中,使用 Vite 可以显著提高开发效率。通过即时热更新和按需编译,开发者可以立即看到代码修改的效果,减少等待时间。

        • 在生产环境中,通过 Vite 的优化策略,可以生成高性能的构建结果。静态资源的优化处理和基于 Rollup 的构建方式,确保了应用的快速加载和高效运行,提升了用户体验。

      综上所述,Vite 通过即时热更新、按需编译和依赖预构建等技术,在开发环境中提供了快速的开发体验。同时,在生产环境中,Vite 利用静态资源优化、Rollup 构建和 Tree Shaking 等策略,生成高性能的构建结果,提升了应用的运行效率和用户体验。

      48 对响应式布局的理解

      (1)考察点分析

      • 基础概念:了解响应式布局的基本原理和特点。
      • 实现方式:掌握实现响应式布局的常用技术和方法。
      • 实际应用:理解响应式布局在实际项目中的应用场景和设计策略。

      (2)最终答案

      1. 基础概念

        • 响应式布局
          响应式布局是一种网页设计方法,使网页能够在不同设备和屏幕尺寸上自适应地调整布局和样式。其核心思想是通过流动网格、灵活图像和 CSS 媒体查询等技术,使网页在各种设备上显示效果一致,用户体验良好。

        • 特点
          响应式布局具有以下特点:

          1. 自适应:页面内容能够根据不同设备的屏幕尺寸自动调整,确保在各种设备上都能良好展示。
          2. 统一性:避免为不同设备单独设计和开发多个版本的网页,减少开发和维护成本。
          3. 灵活性:通过灵活的布局和样式,使网页能够适应未来的设备变化。
      2. 实现方式

        • 流动网格(Fluid Grid)
          使用百分比而不是固定像素来定义布局的宽度,使布局能够根据屏幕大小进行调整。例如:

          .container {
            width: 100%;
            max-width: 1200px;
            margin: 0 auto;
          }
          
          .column {
            float: left;
            width: 50%; /* 使用百分比定义宽度 */
          }
          
        • 灵活图像(Flexible Images)
          使用 CSS 设置图像的最大宽度为 100%,使图像能够根据容器的宽度自适应调整。例如:

          img {
            max-width: 100%;
            height: auto;
          }
          
        • 媒体查询(Media Queries)
          使用 CSS3 媒体查询,根据设备的不同特性(如宽度、高度、分辨率等)应用不同的样式。例如:

          @media (max-width: 768px) {
            .column {
              width: 100%; /* 在小屏幕设备上,列宽度为 100% */
            }
          }
          
          @media (min-width: 769px) {
            .column {
              width: 50%; /* 在大屏幕设备上,列宽度为 50% */
            }
          }
          
      3. 实际应用

        • 在开发过程中,通过响应式布局可以确保网页在各种设备(如手机、平板、桌面电脑)上都能提供良好的用户体验。例如,使用流行的前端框架(如 Bootstrap、Tailwind CSS 等),可以快速实现响应式布局,提升开发效率和代码复用性。

        • 实际项目中,响应式布局的应用场景包括但不限于:

          1. 响应式导航栏:根据屏幕宽度调整导航栏的布局和样式,使其在小屏设备上变为汉堡菜单。
          2. 响应式图片和视频:确保图片和视频在不同设备上按比例缩放,提供最佳观看体验。
          3. 响应式表格:在小屏设备上,将表格内容调整为更适合阅读的格式,避免横向滚动。

      综上所述,响应式布局是一种重要的网页设计技术,通过流动网格、灵活图像和 CSS 媒体查询等方法,实现网页在不同设备上的自适应布局。理解和掌握响应式布局技术,可以显著提升网页的用户体验和跨设备兼容性。

      49 媒体查询media的概念和作用

      (1)考察点分析

      • 基础概念:了解媒体查询的基本原理和语法。
      • 应用场景:理解媒体查询在响应式设计中的应用场景和作用。
      • 实际应用:掌握如何在项目中使用媒体查询实现响应式布局。

      (2)最终答案

      1. 基本概念

        • 媒体查询
          媒体查询是 CSS3 的一个特性,通过 @media 规则定义,根据设备的不同特性应用不同的样式,从而实现响应式设计。

        • 语法
          媒体查询的基本语法包括媒体类型和一个或多个媒体特性。例如,根据屏幕宽度调整样式:

          @media screen and (max-width: 768px) {
            /* 在屏幕宽度不超过 768px 时应用的样式 */
            .container {
              width: 100%;
            }
          }
          
      2. 应用场景

        • 响应式布局
          使用媒体查询根据设备的屏幕大小调整布局,使网页在各种设备上都能良好展示。例如,在大屏幕设备上使用多列布局,在小屏幕设备上使用单列布局:

          @media (min-width: 769px) {
            .column {
              width: 50%;
            }
          }
          
          @media (max-width: 768px) {
            .column {
              width: 100%;
            }
          }
          
        • 按需加载样式
          在不同设备上加载不同的样式文件,减少不必要的资源加载。例如,为移动设备加载轻量级的样式:

          <link rel="stylesheet" media="screen and (max-width: 768px)" href="mobile.css">
          <link rel="stylesheet" media="screen and (min-width: 769px)" href="desktop.css">
          
        • 设备特性优化
          针对高分辨率屏幕、触摸屏等设备特性优化用户体验。例如,为 Retina 屏幕加载高分辨率的图片:

          @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
            .logo {
              background-image: url('[email protected]');
            }
          }
          
      3. 实际应用

        • 在项目中使用媒体查询调整布局和样式,以实现响应式设计。以下是一个完整的示例:
          DOCTYPE html>
          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Responsive Designtitle>
            <style>
              body {
                font-family: Arial, sans-serif;
              }
              
              .container {
                width: 80%;
                margin: 0 auto;
              }
              
              .header, .footer {
                background-color: #f1f1f1;
                text-align: center;
                padding: 1em 0;
              }
              
              .main {
                display: flex;
                flex-wrap: wrap;
              }
              
              .column {
                flex: 1;
                padding: 1em;
              }
          
              /* 大屏幕设备样式 */
              @media (min-width: 769px) {
                .column {
                  width: 50%;
                }
              }
          
              /* 小屏幕设备样式 */
              @media (max-width: 768px) {
                .column {
                  width: 100%;
                }
              }
            style>
          head>
          <body>
            <div class="container">
              <div class="header">Headerdiv>
              <div class="main">
                <div class="column">Column 1div>
                <div class="column">Column 2div>
              div>
              <div class="footer">Footerdiv>
            div>
          body>
          html>
          

      在以上示例中,我们通过媒体查询实现了响应式布局:

      • 在大屏幕设备(宽度大于 768px)上,页面采用两列布局,每列宽度为 50%。
      • 在小屏幕设备(宽度不超过 768px)上,页面采用单列布局,每列宽度为 100%。

      通过媒体查询,可以根据不同设备的特性调整样式,使网页在各种设备上都能提供良好的用户体验。结合其他响应式技术(如流动网格、灵活图像等),可以进一步提升页面的跨设备兼容性和用户体验。

      50 React 和 Vue 的区别?

      (1)考察点分析

      • 框架基础:理解 React 和 Vue 的基本概念和设计哲学。
      • 技术细节:掌握 React 和 Vue 在技术实现和功能上的主要区别。
      • 实际应用:了解 React 和 Vue 在实际项目中的应用场景和优势。

      (2)最终答案

      1. 框架基础
      • React

        • 由 Facebook 开发和维护,是一个用于构建用户界面的 JavaScript 库。
        • 采用组件化和单向数据流,主要用于构建复杂、动态的单页应用(SPA)。
        • 使用 JSX(JavaScript XML)来编写组件,使得 HTML 和 JavaScript 紧密结合。
      • Vue

        • 由尤雨溪开发,是一个用于构建用户界面的渐进式框架。
        • 设计简洁,易于上手,适用于各种规模的项目。
        • 使用基于 HTML 的模板语法,支持 Vue 的指令(如 v-bindv-if)来绑定数据和控制视图。
      1. 技术细节
      • 模板语法

        • React:使用 JSX,允许在 JavaScript 中直接编写 HTML 结构。
          const element = 

          Hello, world!

          ;
        • Vue:使用基于 HTML 的模板语法,支持 Vue 指令。
          <template>
            <h1>{{ message }}h1>
          template>
          <script>
          export default {
            data() {
              return {
                message: 'Hello, world!'
              };
            }
          };
          script>
          
      • 数据绑定

        • React:单向数据流,数据从父组件传递到子组件,通过 props 进行传递和更新。
        • Vue:双向数据绑定,通过 v-model 指令可以实现数据和视图的双向绑定。
      • 组件体系

        • React:一切皆组件,组件是函数或类,状态管理和生命周期方法在组件内部定义。
          class MyComponent extends React.Component {
            constructor(props) {
              super(props);
              this.state = { count: 0 };
            }
            render() {
              return 
          {this.state.count}
          ; } }
        • Vue:组件化结构清晰,组件是对象,支持模板、脚本和样式的单文件组件(.vue 文件)。
          <template>
            <div>{{ count }}div>
          template>
          <script>
          export default {
            data() {
              return {
                count: 0
              };
            }
          };
          script>
          <style scoped>
          div {
            color: red;
          }
          style>
          
      • 状态管理

        • React:使用内置的 useStateuseReducer 等 Hooks 管理状态。对于复杂的状态管理,可以使用外部库(如 Redux、MobX)。
          const [count, setCount] = useState(0);
          
        • Vue:使用内置的 reactiveref 等响应式 API。Vuex 是 Vue 官方的状态管理库,用于集中式管理应用状态。
          const state = reactive({ count: 0 });
          
      • 路由和生态系统

        • React:React Router 是社区常用的路由解决方案。React 生态系统丰富,有大量第三方库和工具。
        • Vue:Vue Router 是官方提供的路由解决方案,与 Vue 深度集成。Vue 生态系统包含 Vuex、Vue CLI 等工具和插件,易于搭建和扩展项目。
      1. 实际应用
      • React

        • 适用于大型复杂应用,需要灵活高效的状态管理和自定义逻辑。
        • 由于其广泛使用和成熟生态,适合团队开发和长期维护的项目。
      • Vue

        • 适用于中小型项目和快速原型开发,易于上手,开发效率高。
        • 由于其渐进式架构,适合逐步引入到现有项目中。

      综上所述,React 和 Vue 各有优点,选择合适的框架应根据项目需求和团队技术栈进行决定。

      51 进程和线程的区别

      (1)考察点分析

      • 基础概念:了解进程和线程的基本定义及其关系。
      • 技术细节:掌握进程和线程在操作系统中的主要区别,包括资源管理、通信方式等。
      • 实际应用:理解进程和线程在实际应用中的优缺点和适用场景。

      (2)最终答案

      1. 基础概念
      • 进程
        • 进程是操作系统中的一个执行单元,每个进程拥有独立的内存空间、代码和数据,是程序的一次执行。
      • 线程
        • 线程是进程中的一个执行单元,一个进程可以包含多个线程,线程共享进程的地址空间和系统资源,但拥有独立的执行栈和程序计数器。
      1. 资源管理
      • 进程
        • 拥有独立的内存空间和资源,进程之间的通信需要通过进程间通信(IPC)机制。
        • 进程切换的开销较大,包括保存和恢复进程上下文等操作。
      • 线程
        • 共享进程的内存空间和资源,线程之间可以直接读写共享数据。
        • 线程切换的开销较小,因为线程共享进程的资源,切换时只需保存和恢复线程的上下文。
      1. 并发编程
      • 进程
        • 适用于需要高度隔离和独立性的场景,如多进程的服务器模型,每个进程负责处理一个请求。
        • 进程之间的通信相对复杂,但可以通过操作系统提供的 IPC 机制实现。
      • 线程
        • 适用于需要高效并发和共享资源的场景,如多线程的 Web 服务器、图形界面程序等。
        • 线程之间共享进程的资源,因此通信和数据共享更加简单和高效,但需要注意线程安全问题。

      通过以上解释,可以清晰地理解进程和线程的区别,以及它们在操作系统和并发编程中的应用场景和特点。

      51 http 状态码有哪些?

      (1)考察点分析

      • 操作系统基础:理解计算机操作系统中进程和线程的基本概念。
      • 资源管理:区分进程和线程在资源管理上的差异。
      • 并发编程:理解进程和线程在并发编程中的应用场景和特点。

      (2)最终答案

      1. 基础概念
      • 进程
        • 进程是操作系统中的一个执行单元,每个进程拥有独立的内存空间、代码和数据,是程序的一次执行。
      • 线程
        • 线程是进程中的一个执行单元,一个进程可以包含多个线程,线程共享进程的地址空间和系统资源,但拥有独立的执行栈和程序计数器。
      1. 资源管理
      • 进程
        • 拥有独立的内存空间和资源,进程之间的通信需要通过进程间通信(IPC)机制。
        • 进程切换的开销较大,包括保存和恢复进程上下文等操作。
      • 线程
        • 共享进程的内存空间和资源,线程之间可以直接读写共享数据。
        • 线程切换的开销较小,因为线程共享进程的资源,切换时只需保存和恢复线程的上下文。
      1. 并发编程
      • 进程
        • 适用于需要高度隔离和独立性的场景,如多进程的服务器模型,每个进程负责处理一个请求。
        • 进程之间的通信相对复杂,但可以通过操作系统提供的 IPC 机制实现。
      • 线程
        • 适用于需要高效并发和共享资源的场景,如多线程的 Web 服务器、图形界面程序等。
        • 线程之间共享进程的资源,因此通信和数据共享更加简单和高效,但需要注意线程安全问题。

      通过以上解释,可以清晰地理解进程和线程的区别,以及它们在操作系统和并发编程中的应用场景和特点。

      52 快速排序算法

      (1)考察点分析

      • 排序算法:了解快速排序算法的基本思想和实现方式。
      • 时间复杂度:理解快速排序算法的时间复杂度。
      • 代码实现:掌握快速排序算法的实现步骤和关键代码。

      (2)最终答案

      1. 基本概念
        快速排序是一种常见的排序算法,基于分治法的思想。其基本思想是选择一个基准元素,将数组分成两个子数组,小于基准元素的放在左边,大于基准元素的放在右边,然后对子数组进行递归排序。

      2. 算法步骤

      • 选择基准元素:从数组中选择一个元素作为基准元素。
      • 分区操作:将数组中小于基准元素的放在左边,大于基准元素的放在右边,基准元素放在中间。
      • 递归排序:对左右两个子数组进行递归排序。
      1. 代码实现
        下面是快速排序算法的 JavaScript 实现示例:
      function quickSort(arr) {
          if (arr.length <= 1) {
              return arr;
          }
      
          const pivot = arr[0];
          const left = [];
          const right = [];
      
          for (let i = 1; i < arr.length; i++) {
              if (arr[i] < pivot) {
                  left.push(arr[i]);
              } else {
                  right.push(arr[i]);
              }
          }
      
          return [...quickSort(left), pivot, ...quickSort(right)];
      }
      
      const arr = [5, 3, 7, 2, 8, 4, 1];
      const sortedArr = quickSort(arr);
      console.log(sortedArr); // 输出:[1, 2, 3, 4, 5, 7, 8]
      

      53 二分查找算法

      (1)考察点分析

      • 查找算法:了解二分查找算法的基本思想和实现方式。
      • 时间复杂度:理解二分查找算法的时间复杂度。
      • 应用场景:掌握二分查找算法在有序数组中的应用场景。

      (2)最终答案

      1. 基本概念
      二分查找算法是一种高效的查找算法,适用于有序数组。其基本思想是通过每一次比较将查找范围缩小一半,从而快速定位目标值。

      2. 算法步骤

      1. 初始化左右边界:将左边界设为数组起始位置,右边界设为数组末尾位置。
      2. 循环查找:在左右边界之间循环,计算中间位置 mid,比较目标值和中间值:
        • 如果目标值等于中间值,则返回 mid。
        • 如果目标值小于中间值,则更新右边界为 mid - 1。
        • 如果目标值大于中间值,则更新左边界为 mid + 1。
      3. 如果左边界大于右边界,则表示查找失败,返回 -1。
      function binarySearch(arr, target) {
          let left = 0;
          let right = arr.length - 1;
      
          while (left <= right) {
              const mid = Math.floor((left + right) / 2);
              const midValue = arr[mid];
      
              if (midValue === target) {
                  return mid; // 找到目标值,返回索引
              } else if (midValue < target) {
                  left = mid + 1; // 目标值在右半边
              } else {
                  right = mid - 1; // 目标值在左半边
              }
          }
      
          return -1; // 未找到目标值
      }
      
      // 示例
      const array = [1, 3, 5, 7, 9, 11, 13, 15];
      const target = 9;
      const index = binarySearch(array, target);
      console.log(index); // 输出:4
      

      3. 应用场景
      二分查找算法适用于有序数组,特别是在静态数据集中查找目标值的场景。例如,在排序好的数组中查找某个元素的位置,或者判断某个元素是否存在于数组中。

你可能感兴趣的:(前端,面试,职场和发展)