返回

领悟JS经典面试题,进阶前端开发之路

见解分享

前言

JavaScript (JS) 作为一门流行的编程语言,在前端开发领域发挥着至关重要的作用。它能够为网页带来交互性、动画效果和动态内容。因此,对于前端开发人员来说,掌握JS是必不可少的。在求职面试中,JS也是经常被问到的一个话题。

经典JS面试题

为了帮助您更好地理解JS并为面试做好准备,我整理了一些经典的JS面试题,涵盖基础和高级水平。这些问题旨在测试您对JS核心概念和原理的掌握程度,并帮助您发现自己的知识盲点。

基础题

  1. 什么是JS中的闭包?
  2. JS中的原型链是什么?
  3. JS中的事件循环是如何工作的?
  4. JS中的作用域是如何划分的?
  5. JS中的变量提升是如何工作的?
  6. JS中的this是什么?

高级题

  1. 如何在JS中创建自定义事件?
  2. 如何在JS中使用代理模式?
  3. 如何在JS中使用发布/订阅模式?
  4. 如何在JS中使用模块化编程?
  5. 如何在JS中使用异步编程?

解答与示例

基础题解答

  1. 闭包

    闭包是指一个内部函数可以访问其外部函数作用域中的变量,即使外部函数已经执行完毕。闭包的存在使得我们可以访问外部函数的作用域,从而实现一些特殊的效果。例如,闭包可以被用来实现私有变量、柯里化函数、惰性求值等等。

    function outer() {
      let count = 0;
    
      function inner() {
        count++;
        console.log(count);
      }
    
      return inner;
    }
    
    const innerFunction = outer();
    
    innerFunction(); // 1
    innerFunction(); // 2
    innerFunction(); // 3
    

    在这个例子中,内部函数inner()可以访问外部函数outer()中的变量count,即使outer()已经执行完毕。每次调用inner(),count的值都会增加1。

  2. 原型链

    原型链是指对象继承的一种机制。在JS中,每个对象都有一个原型对象,该原型对象包含了该对象的所有属性和方法。如果一个对象没有某个属性或方法,它会沿着原型链向上查找,直到找到该属性或方法。

    const person = {
      name: 'John Doe',
      age: 30
    };
    
    const employee = {
      salary: 100000
    };
    
    employee.__proto__ = person;
    
    console.log(employee.name); // John Doe
    console.log(employee.age); // 30
    console.log(employee.salary); // 100000
    

    在这个例子中,employee对象继承了person对象的属性和方法。当我们访问employee对象的name和age属性时,会直接找到这些属性的值。而当我们访问employee对象的salary属性时,会沿着原型链向上查找,直到找到该属性。

  3. 事件循环

    事件循环是指JS运行时的一种机制,它负责处理事件并执行相应的任务。事件循环是一个不断重复的过程,它不断地从事件队列中获取事件,然后执行相应的事件处理函数。

    setTimeout(() => {
      console.log('This will be executed after 1 second');
    }, 1000);
    
    console.log('This will be executed immediately');
    

    在这个例子中,setTimeout()函数将一个事件添加到事件队列中,该事件将在1秒后执行。console.log()函数则会立即执行。因此,上面的代码会先输出"This will be executed immediately",然后在1秒后输出"This will be executed after 1 second"。

  4. 作用域

    作用域是指一个变量或函数可以被访问的范围。在JS中,作用域分为全局作用域和局部作用域。全局作用域是指在整个程序中都可以访问的变量或函数,而局部作用域是指只在某个特定块或函数中可以访问的变量或函数。

    let globalVariable = 10;
    
    function outer() {
      let localVariable = 20;
    
      function inner() {
        console.log(globalVariable);
        console.log(localVariable);
      }
    
      inner();
    }
    
    outer();
    

    在这个例子中,globalVariable是全局变量,可以在outer()函数和inner()函数中访问。localVariable是局部变量,只能在outer()函数和inner()函数中访问。

  5. 变量提升

    变量提升是指在JS中,变量的声明会被提升到函数或块的顶部。这意味着,变量可以在声明之前使用。然而,变量提升只对变量的声明有效,而对变量的赋值没有影响。

    console.log(x); // undefined
    var x = 10;
    

    在这个例子中,x被提升到了函数的顶部,但它的值还没有被赋值。因此,当我们尝试在声明之前使用x时,它的值为undefined。

  6. this关键字

    this关键字是指当前对象。它的值取决于函数的调用方式。在普通函数中,this的值是全局对象。在对象的方法中,this的值是该对象。在事件处理函数中,this的值是触发该事件的元素。

    console.log(this); // Window
    
    const person = {
      name: 'John Doe',
      age: 30,
      greet() {
        console.log(this); // { name: 'John Doe', age: 30 }
      }
    };
    
    person.greet();
    
    document.addEventListener('click', function() {
      console.log(this); // <html>
    });
    

    在这个例子中,this的值取决于函数的调用方式。在第一行,this的值是全局对象。在第二行,this的值是person对象。在第三行,this的值是触发click事件的元素。

高级题解答

  1. 如何创建自定义事件?

    可以通过使用CustomEvent()构造函数来创建自定义事件。该构造函数接受两个参数:事件类型和事件数据。

    const customEvent = new CustomEvent('my-custom-event', {
      detail: {
        data: 'Hello world!'
      }
    });
    
    dispatchEvent(customEvent);
    

    在这个例子中,我们创建了一个名为my-custom-event的自定义事件,并使用detail属性传递了数据。然后,我们使用dispatchEvent()方法触发了该事件。

  2. 如何使用代理模式?

    代理模式是一种设计模式,它允许我们在对象之间创建一个间接的引用。这使得我们可以更灵活地控制对对象的访问,并隔离对象之间的耦合。

    class RealSubject {
      doSomething() {
        console.log('RealSubject: Doing something...');
      }
    }
    
    class Proxy {
      constructor(realSubject) {
        this.realSubject = realSubject;
      }
    
      doSomething() {
        console.log('Proxy: Doing something before...');
        this.realSubject.doSomething();
        console.log('Proxy: Doing something after...');
      }
    }
    
    const realSubject = new RealSubject();
    const proxy = new Proxy(realSubject);
    
    proxy.doSomething();
    

    在这个例子中,Proxy类作为RealSubject类的代理,在RealSubject类的方法执行前后执行了一些额外的操作。

  3. 如何使用发布/订阅模式?

    发布/订阅模式是一种设计模式,它允许对象之间进行松散耦合的通信。发布者对象可以发布事件,而订阅者对象可以订阅这些事件。当发布者对象发布事件时,订阅者对象会自动收到通知并执行相应的处理。

    class EventEmitter {
      constructor() {
        this.subscribers = [];
      }
    
      subscribe(subscriber) {
        this.subscribers.push(subscriber);
      }
    
      publish(data) {
        this.subscribers.forEach(subscriber => subscriber(data));