nodejs 语言特性(面试系列1)

一、JavaScript 基础(重中之重)

1. 语言特性

  • 数据类型

  • 基本类型(null/undefined 区别)、引用类型、类型转换(隐式转换规则)。

  • 一、基本数据类型(7 种)
    
    Undefined‌
    
    表示未定义的变量(仅声明未赋值)
    示例:let a; console.log(a); // undefined
    
    Null‌
    
    表示空值或对象占位符
    示例:let b = null;
    
    Boolean‌
    
    逻辑值(true/false)
    示例:let isValid = true;
    
    Number‌
    
    整数或浮点数(如 42、3.14)
    特殊值:NaN(非数字)、Infinity(无穷大)
    
    String‌
    
    文本数据(如 "Hello")
    支持模板字符串:`Count: ${count}`
    
    Symbol‌(ES6 新增)
    
    唯一且不可变的值,用于对象属性键
    示例:const key = Symbol('unique_id');
    
    BigInt‌(ES2020 新增)
    
    处理超大整数(超过 Number 安全范围)
    示例:const bigNum = 9007199254740991n;
    
    二、引用数据类型(1 种)
    Object‌
    复杂数据结构,包含:
    普通对象 {}(如 { name: 'Alice' })
    数组 [](如 [1, 2, 3])
    函数 function(){}
    日期 Date、正则 RegExp 等内置对象
  • 原型与原型链(prototype__proto__)

  • prototype__proto__、继承实现(构造函数继承、原型链继承、组合继承等)。
    每个JavaScript函数都有一个prototype属性(箭头函数除外),该属性指向一个对象,称为‌原型对象‌。
    
    当函数作为构造函数使用时(使用new调用),通过该构造函数创建的对象会继承其原型对象的属性和方法。
    
    function Person(name) {
      this.name = name;
    }
    
    // 在原型上添加方法
    Person.prototype.greet = function() {
      return `Hello, I'm ${this.name}`;
    };
    
    const alice = new Person('Alice');
    console.log(alice.greet()); // "Hello, I'm Alice"
    
    原型链(Prototype Chain)
    当访问对象的属性时,JavaScript会:
    
    在对象自身属性中查找
    如果没找到,则沿着__proto__属性到其原型对象中查找
    如果还没找到,则继续沿着原型的原型查找
    直到找到属性或到达原型链顶端(Object.prototype.__proto__ === null)
    
    console.log(alice.toString()); // 来自Object.prototype
    
    
    
    原型链是JavaScript实现继承的基础
    
    function Employee(name, title) {
      Person.call(this, name); // 继承属性
      this.title = title;
    }
    
    // 设置原型链继承
    Employee.prototype = Object.create(Person.prototype);
    Employee.prototype.constructor = Employee;
    
    // 添加子类方法
    Employee.prototype.work = function() {
      return `${this.name} is working as ${this.title}`;
    };
    
    const bob = new Employee('Bob', 'Developer');
    console.log(bob.greet()); // "Hello, I'm Bob" (继承自Person)
    console.log(bob.work());  // "Bob is working as Developer"
    
    原型上的属性和方法被所有实例共享,节省内存:
    
    function Car(model) {
      this.model = model;
    }
    
    // 所有Car实例共享wheelCount和drive方法
    Car.prototype.wheelCount = 4;
    Car.prototype.drive = function() {
      return `${this.model} is driving`;
    };
    
    const car1 = new Car('Tesla');
    const car2 = new Car('Toyota');
    console.log(car1.wheelCount); // 4
    console.log(car2.wheelCount); // 4
    

  • 作用域与闭包

  • 词法作用域、var/let/const 区别、闭包的应用场景(如函数防抖 / 节流)。
    JavaScript采用‌词法作用域‌(静态作用域),即变量的作用域在代码编写时就已经确定,而非运行时确定。
    主要特点包括:
    
    ‌1.作用域层级‌:
    全局作用域(Global Scope)
    函数作用域(Function Scope)
    块级作用域(Block Scope,ES6新增)
    
    ‌2.作用域链查找规则‌:
    从当前作用域开始查找变量
    如果找不到,向上一级作用域继续查找
    直到全局作用域,若仍未找到则报错
    
    
    let globalVar = 'global';
    
    function outer() {
      let outerVar = 'outer';
      
      function inner() {
        let innerVar = 'inner';
        console.log(globalVar); // 可以访问所有外层变量
      }
      
      inner();
    }
    
    特性varletconst
    作用域函数作用域或全局作用域块级作用域块级作用域
    变量提升会提升(值为undefined)存在暂时性死区(TDZ)存在暂时性死区(TDZ)
    重复声明允许不允许不允许
    值可变性可变可变不可变(引用类型内容可变)
    全局属性会成为window属性不会成为window属性

    不会成为window属性

  • 闭包是指‌函数与其词法环境的组合‌,即使外部函数已执行完毕,内部函数仍能访问外部函数的变量
    
    
    function createCounter() {
      let count = 0; // 闭包保护的变量
      
      return {
        increment: () => ++count,
        getCount: () => count
      };
    }
    
    const counter = createCounter();
    counter.increment(); // count = 1
    
    通过闭包实现类似私有变量的效果
    
    function createPrivateStore() {
      let data = {};
      
      return {
        set(key, value) {
          data[key] = value;
        },
        get(key) {
          return data[key];
        }
      };
    }
    
    函数防抖(Debounce)
    限制函数在短时间内频繁触发
    
    function debounce(fn, delay) {
      let timer = null;
      
      return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
      };
    }
    
    window.addEventListener('resize', debounce(() => {
      console.log('窗口大小调整结束');
    }, 300));
    
    函数节流(Throttle)
    保证函数在一定时间内只执行一次
    
    function throttle(fn, interval) {
      let lastTime = 0;
      
      return function(...args) {
        const now = Date.now();
        if (now - lastTime >= interval) {
          fn.apply(this, args);
          lastTime = now;
        }
      };
    }
    
    window.addEventListener('scroll', throttle(() => {
      console.log('滚动事件处理');
    }, 200));
    
  • 异步编程

    • 回调函数(Callback Hell 问题)。

      回调函数是JavaScript异步编程的基础形式,通过将函数作为参数传递来实现异步操作完成后的处理。
      
      function fetchData(callback) {
        setTimeout(() => {
          callback('Data received');
        }, 1000);
      }
      
      fetchData((data) => {
        console.log(data); // 1秒后输出"Data received"
      });
      
       Callback Hell(回调地狱)
      当多个异步操作需要顺序执行时,嵌套回调会导致代码难以维护
      
      getUser(userId, (user) => {
        getOrders(user.id, (orders) => {
          getOrderDetails(orders[0].id, (details) => {
            getProductInfo(details.productId, (product) => {
              console.log(product);
              // 更多嵌套...
            });
          });
        });
      });
      
    • Promise(状态机、then/catch/finally、链式调用)。

      1. Promise核心概念
      Promise是ES6引入的异步编程解决方案,代表一个异步操作的最终完成或失败。
      
      ‌三种状态‌:
      
      pending:初始状态
      fulfilled:操作成功完成
      rejected:操作失败
      
      
      const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          Math.random() > 0.5 
            ? resolve('Success!') 
            : reject(new Error('Failed'));
        }, 1000);
      });
      
      promise
        .then((result) => console.log(result))
        .catch((error) => console.error(error))
        .finally(() => console.log('Done'));
      
      Promise的核心优势是链式调用,解决回调地狱问题
      
      function getUser(userId) {
        return new Promise((resolve) => resolve({ id: userId }));
      }
      
      function getOrders(userId) {
        return new Promise((resolve) => resolve([{ id: 'order1' }]));
      }
      
      getUser(123)
        .then(user => getOrders(user.id))
        .then(orders => console.log(orders))
        .catch(error => console.error(error));
      
    • Async/Await(与 Promise 的区别、错误处理)。

      Async/Await是建立在Promise之上的语法糖,使异步代码看起来像同步代码
      
      async function fetchData() {
        try {
          const response = await fetch('api/data');
          const data = await response.json();
          console.log(data);
        } catch (error) {
          console.error('Error:', error);
        }
      }
      
      特性PromiseAsync/Await
      语法结构链式调用同步写法
      错误处理.catch()方法try/catch块
      返回值必须返回Promise自动包装为Promise
      可读性中等
    • 事件循环(Event Loop)

    • Node.js 与浏览器 Event Loop 的差异
      1. 浏览器Event Loop
      浏览器中的事件循环处理顺序:
      
      执行同步代码(调用栈)
      执行当前所有微任务(Microtasks)
      Promise回调
      MutationObserver
      queueMicrotask
      渲染(如有需要)
      执行一个宏任务(Macrotask)
      setTimeout/setInterval
      I/O操作
      UI渲染
      事件回调
      重复循环
      
      
      2. Node.js Event Loop
      Node.js的事件循环分为多个阶段:
      
         ┌───────────────────────┐
      ┌─>│        timers         │ (setTimeout/setInterval)
      │  └──────────┬────────────┘
      │  ┌──────────┴────────────┐
      │  │     pending callbacks │ (I/O回调)
      │  └──────────┬────────────┘
      │  ┌──────────┴────────────┐
      │  │     idle, prepare     │ (内部使用)
      │  └──────────┬────────────┘
      │  ┌──────────┴────────────┐
      │  │         poll          │ (检索新I/O事件)
      │  └──────────┬────────────┘
      │  ┌──────────┴────────────┐
      │  │        check          │ (setImmediate)
      │  └──────────┬────────────┘
      │  ┌──────────┴────────────┐
      └──┤    close callbacks    │ (关闭事件回调)
         └───────────────────────┘
      
      
      
      特性浏览器Node.js
      微任务执行时机每个宏任务之后每个阶段结束后
      setImmediate不支持支持
      process.nextTick不支持支持(优先级高于微任务)
      渲染时机在微任务之后不适用
      微任务(Microtask)与宏任务(Macrotask)的执行顺序。
  • ES6+ 新特性

    • 模块化(import/export 与 CommonJS 的区别)。

    • 特性CommonJS (require/module.exports)ES Modules (import/export)
      加载方式运行时同步加载编译时静态分析,异步加载
      导出类型动态导出(可条件导出)静态导出(必须顶层导出)
      值引用导出值的拷贝导出值的实时绑定(live binding)
      循环引用处理支持但可能部分未初始化支持且保持引用关系
      文件扩展名.js/.cjs.mjs 或 package.json 中 type: "module"
      顶级this指向当前模块undefined
      Node.js支持原生支持需要显式启用或在package.json配置
      ‌CommonJS示例‌:
      // math.js
      module.exports = {
        add: (a, b) => a + b
      };
      
      // app.js
      const math = require('./math');
      console.log(math.add(2, 3));
      
      
      
      ‌ES Modules示例‌:
      
      // math.mjs
      export const add = (a, b) => a + b;
      
      // app.mjs
      import { add } from './math.mjs';
      console.log(add(2, 3));
      
    • 箭头函数(this 绑定、不能用作构造函数)。

      ‌词法this绑定‌:
      
      function Person() {
        this.age = 0;
        
        // 传统函数
        setInterval(function growUp() {
          this.age++; // 错误!这里的this指向全局或undefined
        }, 1000);
        
        // 箭头函数
        setInterval(() => {
          this.age++; // 正确!继承Person的this
        }, 1000);
      }
      
      不能作为构造函数‌:
      
      const Foo = () => {};
      new Foo(); // TypeError: Foo is not a constructor
      
      
      没有prototype属性‌:
      
      console.log((() => {}).prototype); // undefined
      
      不能使用arguments对象‌:
      
      const fn = () => console.log(arguments);
      fn(1, 2); // ReferenceError: arguments is not defined
      
      
      ‌简写语法‌:
      
      const double = x => x * 2;
      const sum = (a, b) => a + b;
      
      
    • Set/Map

      ‌Set‌:值唯一的集合
      
      const set = new Set([1, 2, 3, 3]);
      set.add(4).add(2); // 2不会被重复添加
      console.log(set.has(3)); // true
      set.forEach(v => console.log(v));
      
      
      ‌Map‌:键值对集合(键可以是任意类型)
      const map = new Map();
      map.set('name', 'Alice');
      map.set({ id: 1 }, 'Complex Key');
      console.log(map.get('name')); // 'Alice'
      for (let [key, value] of map) {
        console.log(key, value);
      }
      

      解构赋值

      数组解构‌:
      const [first, , third] = [1, 2, 3];
      console.log(first, third); // 1, 3
      
      对象解构‌:
      const { name, age: userAge = 18 } = { name: 'Bob' };
      console.log(name, userAge); // 'Bob', 18
      
      ‌函数参数解构‌:
      function greet({ name, age }) {
        return `Hello ${name}, you're ${age} years old`;
      }
      greet({ name: 'Alice', age: 25 });
      
    • Symbol

      Symbol
      唯一标识符,常用于:
      
      创建对象唯一属性键
      实现元编程
      
      const id = Symbol('id');
      const user = {
        [id]: 123,
        name: 'Alice'
      };
      console.log(user[id]); // 123
      console.log(Object.keys(user)); // ['name'] (Symbol属性不可枚举)
      
    • Proxy/Reflect 

      ‌Proxy‌:对象操作拦截器
      
      const target = {};
      const handler = {
        get(obj, prop) {
          return prop in obj ? obj[prop] : 37;
        }
      };
      const p = new Proxy(target, handler);
      p.a = 1;
      console.log(p.a, p.b); // 1, 37
      
      Reflect‌:对象操作工具集
      
      const obj = { x: 1, y: 2 };
      console.log(Reflect.has(obj, 'x')); // true
      Reflect.set(obj, 'z', 3);
      console.log(obj.z); // 3
      
  • 关键总结

  • 模块化选择‌:

    • 新项目推荐使用ES Modules
    • 旧项目逐步迁移
    • 注意文件扩展名和package.json配置
  • 箭头函数适用场景‌:

    • 需要词法this绑定的回调函数
    • 简短的单行函数
    • 避免在需要动态this的场景使用
  • 现代数据结构优势‌:

    • Set用于值唯一性处理
    • Map优于普通对象当键需要非字符串时
    • Symbol提供安全的属性键
  • 元编程能力‌:

    • Proxy实现高级对象拦截
    • Reflect提供可靠的基础操作
    • 两者结合可实现强大的元编程模式
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

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

    余额充值