前端面试题(十二)

63. JavaScript 模块化

  • 什么是 JavaScript 模块化?

    • 模块化 是指将代码分割成独立的模块,每个模块负责一个单一的功能或逻辑。通过模块化,代码更加结构化、易于维护、复用性强。
    • 在 JavaScript 中,模块化的实现经历了多个阶段,从最早的全局作用域到后来的 AMD、CommonJS 和 ES6 模块系统。
  • 常见的模块化方案:

    1. CommonJS:Node.js 中使用的模块化方案,模块通过 module.exports 导出,通过 require 导入。

      • 特点:同步加载,适用于服务器端。
      • 例子
        // 导出模块
        module.exports = function add(a, b) {
          return a + b;
        };
        // 导入模块
        const add = require('./add');
        console.log(add(2, 3)); // 输出 5
        
    2. AMD (Asynchronous Module Definition):一种前端模块化方案,主要用于浏览器环境,模块通过 define 定义,通过 require 导入。

      • 特点:异步加载,适用于浏览器端。
      • 例子
        define(['math'], function(math) {
          console.log(math.add(2, 3));
        });
        
    3. ES6 模块:ES6 标准引入的模块系统,通过 export 导出,通过 import 导入。

      • 特点:支持静态分析,异步加载,适用于浏览器和服务器。
      • 例子
        // 导出模块
        export function add(a, b) {
          return a + b;
        }
        // 导入模块
        import { add } from './math';
        console.log(add(2, 3)); // 输出 5
        

64. 原型链

  • 什么是原型链?

    • 原型链 (Prototype Chain) 是 JavaScript 中实现继承的机制。每个对象都有一个 [[Prototype]] 属性(可以通过 __proto__ 访问),指向它的原型对象。原型对象也可以有自己的原型,形成一个链条,直到指向 null 为止。
    • 当访问一个对象的属性时,如果对象本身没有这个属性,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或到达链条末端 null
  • 例子:

    function Person(name) {
      this.name = name;
    }
    Person.prototype.greet = function() {
      console.log('Hello, ' + this.name);
    };
    
    const alice = new Person('Alice');
    alice.greet(); // 输出 "Hello, Alice"
    console.log(alice.__proto__ === Person.prototype); // true
    
  • 原型链的常见面试问题:

    1. instanceof 的工作原理是什么?

      • instanceof 运算符会沿着对象的原型链查找,判断对象的原型链中是否有构造函数的 prototype 属性。如果有,则返回 true
      • 例子
        console.log(alice instanceof Person); // true
        
    2. 原型链继承的缺点是什么?

      • 所有实例共享同一个原型对象上的属性或方法,如果原型对象上有引用类型的属性,可能会导致实例之间相互影响。
      • 例子
        function Animal() {
          this.traits = ['fur'];
        }
        Animal.prototype.getTraits = function() {
          return this.traits;
        };
        
        const cat = new Animal();
        const dog = new Animal();
        cat.traits.push('claws');
        console.log(dog.getTraits()); // 输出 ["fur", "claws"]
        

65. 浏览器缓存

  • 浏览器缓存的机制有哪些?
    浏览器缓存是提升性能的重要手段,通过将资源缓存到本地,避免重复下载相同的资源。常见的缓存机制有两种:强缓存协商缓存

    1. 强缓存:浏览器根据缓存规则,直接从缓存中读取资源,不与服务器进行通信。常见的 HTTP 头包括:

      • Expires:设置资源的过期时间,使用的是服务器时间,缺点是时间同步问题。
      • Cache-Control:更灵活的缓存控制方式,可以使用 max-age 指定资源的最大缓存时间。
    2. 协商缓存:浏览器在请求资源时,会与服务器进行验证,服务器会根据资源是否更新来决定是返回新的资源还是使用缓存。常见的 HTTP 头包括:

      • Last-Modified / If-Modified-Since:通过文件的最后修改时间来验证资源是否更新。
      • ETag / If-None-Match:通过文件的唯一标识符来验证资源是否更新,优先级高于 Last-Modified
    • 缓存控制的例子:
      Cache-Control: max-age=31536000, public
      ETag: "abc123"
      

66. DOM 操作

  • 如何高效地操作 DOM?
    1. 批量操作 DOM:尽量减少直接操作 DOM 的次数,使用文档片段 (DocumentFragment) 或缓存 DOM 元素来优化性能。

      • 例子
        const fragment = document.createDocumentFragment();
        for (let i = 0; i < 1000; i++) {
          const li = document.createElement('li');
          li.textContent = `Item ${i}`;
          fragment.appendChild(li);
        }
        document.getElementById('list').appendChild(fragment);
        
    2. 使用 innerHTML 代替 createElement:对于创建较简单的 HTML 结构,使用 innerHTML 相较于 createElement 性能更高。

      • 例子
        document.getElementById('list').innerHTML = '<li>Item 1</li><li>Item 2</li>';
        
    3. 减少回流和重绘:尽量减少对 DOM 树结构的频繁修改,特别是涉及布局属性的修改(如宽高、位置等),这些操作会触发回流(重排)和重绘。

      • 例子
        const el = document.getElementById('box');
        el.style.width = '100px';
        el.style.height = '100px';
        el.style.backgroundColor = 'blue'; // 这三行操作会触发多次回流
        
    4. 事件代理:对于动态生成的元素,可以通过事件代理的方式提高性能,而不是给每个元素都绑定事件。

      • 例子
        document.getElementById('list').addEventListener('click', function(event) {
          if (event.target.tagName === 'LI') {
            console.log('Item clicked:', event.target.textContent);
          }
        });
        

67. Vue 响应式原理

  • Vue 的响应式系统是如何工作的?
    Vue 的响应式系统基于 数据劫持发布-订阅模式 来实现。它通过使用 Object.defineProperty 对数据进行劫持,在数据发生变化时触发视图更新。

    1. 数据劫持:当 Vue 实例初始化时,Vue 会递归地遍历数据对象的每一个属性,并使用 Object.defineProperty 将这些属性转换为 getter 和 setter,从而拦截对这些属性的访问和修改。
    2. 依赖收集:当组件渲染时,Vue 会将渲染过程中使用到的数据属性进行依赖收集,并建立依赖关系,当数据发生变化时,会通知对应的观察者(watcher)重新渲染视图。
    3. 发布-订阅模式:当数据发生变化时,setter 会触发依赖更新,执行观察者的更新方法,达到数据驱动视图更新的效果。
    • 示例代码:
      let data = { name: 'Alice' };
      Object.defineProperty(data, 'name', {
        get() {
          console.log('获取 name');
          return name;
        },
        set(newVal) {
          console.log('设置 name 为', newVal);
          name = newVal;
        }
      });
      
      console.log(data.name); // 触发 get
      data.name = 'Bob';      // 触发 set
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小于负无穷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值