(03).前端知识库之JavaScript篇

60 阅读7分钟

一、基础概念

  1. 变量声明方式的区别
  • var:函数作用域,存在变量提升和暂时性死区。
  • let:块作用域,无变量提升,支持结构化声明。
  • const:块作用域,声明后不可修改,必须初始化。
  1. JavaScript基本数据类型
  • 简单类型(原始类型):Number, String, Boolean, Undefined, Null, Symbol(ES6新增)。
  • 复合类型(引用类型):Object, Array, Function, Date, RegExp等。
  1. 简单类型与复合类型的区别
  • 简单类型:值直接存储在变量中,不可变。
  • 复合类型:存储的是指向堆内存对象的引用,可变。
  1. null vs undefined
  • null:表示空指针,主动赋值的结果。
  • undefined:未初始化的变量或不存在的属性,默认值。
  • 判断:null == undefined(真),但 ===(假)。
  1. == vs ===
  • ==:类型不同会强制转换后再比较。
  • ===:严格比较类型和值。
  • 使用场景:===优先,避免类型转换陷阱。
  1. Hoisting(变量提升)
  • 变量声明会被提前到作用域顶部,但初始化不会。
  • 示例:
    console.log(a); // undefined(变量提升)
    var a = 1;
    
  1. this的指向规则
  • 全局作用域:指向全局对象(浏览器为window)。
  • 函数调用:普通函数指向全局,严格模式指向undefined
  • 对象方法:指向调用该方法的对象。
  • 箭头函数:无自己的this,继承自父作用域。
  1. 箭头函数与普通函数的区别
  • 语法简洁,无function关键字。
  • 没有this绑定(继承父作用域)。
  • 不能作为构造函数,没有arguments对象。
  1. calleecaller的作用
  • callee:指向当前函数的引用(arguments.callee)。
  • caller:指向调用当前函数的上下文对象。
  • 示例:
    function foo() {
      console.log(arguments.callee === foo); // true
    }
    

二、函数与作用域

  1. 函数的定义与调用
  • 定义:function, 箭头函数, 表达式, 方法
  • 调用:直接调用、作为参数传递、事件绑定。
  1. 闭包的定义与应用
  • 闭包:函数内部访问外部作用域的变量形成的环境。
  • 应用:数据封装、模块化、私有变量。
  1. 作用域链与原型链
  • 作用域链:查找变量的路径(当前作用域 → 父作用域 → ... → 全局)。
  • 原型链:对象继承的链条(__proto__指向父原型)。
  1. 绑定多个onclick事件

    // 方式1:直接赋值(覆盖)
    btn.onclick = function() { /* 第一个事件 */ };
    btn.onclick = function() { /* 第二个事件 */ };
    
    // 方式2:使用addEventListener(支持多事件)
    btn.addEventListener('click', handler1);
    btn.addEventListener('click', handler2);
    
  2. 异步函数与同步函数

  • 同步:代码按顺序执行,阻塞主线程。
  • 异步:非阻塞,通过回调、Promise、async/await实现。

三、面向对象与原型

  1. 原型与原型链原理
  • 每个对象都有一个__proto__属性,指向其原型对象。
  • 原型链:通过__proto__逐级查找属性和方法。
  1. 继承实现方式
  • ES5:原型链继承(Object.create())和构造函数继承。
  • ES6:class语法糖(基于原型链)。
  1. ES5与ES6继承区别
  • ES5:手动管理原型链,易出错(如共享引用)。
  • ES6:class关键字和super关键字,语法更清晰。
  1. instanceof原理及手动实现
  • 原理:检查对象的原型链是否包含构造函数的prototype
  • 手动实现:
    function myInstanceof(obj, constructor) {
      while (obj !== null) {
        obj = obj.__proto__;
        if (obj === constructor.prototype) return true;
        obj = obj.__proto__;
      }
      return false;
    }
    
  1. new操作符的执行过程

  2. 创建新对象,this指向它。

  3. 执行构造函数代码,初始化属性。

  4. 新对象的__proto__指向构造函数的prototype

  5. JavaScript面向对象特点

  • 基于原型的动态继承。
  • 函数是一等公民(可作为值传递)。
  • 多态通过原型链实现。

四、异步编程与事件循环

  1. 异步实现方式
  • 回调函数、Promise、Generator、async/await。
  1. Promise手写实现

    class MyPromise {
      constructor(executor) {
        this.state = 'pending';
        this.value = undefined;
        this.handlers = [];
        executor(this.resolve.bind(this), this.reject.bind(this));
      }
    
      resolve(val) {
        if (this.state === 'pending') {
          this.state = 'fulfilled';
          this.value = val;
          this.handlers.forEach(h => h.onFulfilled(val));
        }
      }
    
      reject(err) {
        if (this.state === 'pending') {
          this.state = 'rejected';
          this.value = err;
          this.handlers.forEach(h => h.onRejected(err));
        }
      }
    
      then(onFulfilled, onRejected) {
        return new MyPromise((resolve, reject) => {
          this.handlers.push({
            onFulfilled: (val) => {
              try {
                resolve(onFulfilled(val));
              } catch (e) {
                reject(e);
              }
            },
            onRejected: (err) => {
              try {
                reject(onRejected(err));
              } catch (e) {
                reject(e);
              }
            }
          });
        });
      }
    }
    
  2. async/await原理

  • async函数返回Promise,await暂停函数执行直到Promise解决。
  • 实现基于Generator和Promise的组合。
  1. 事件循环机制
  • 宏任务(setTimeout、setInterval、I/O)→ 微任务(Promise.then、async/await)→ 渲染。
  1. 定时器执行差异
    console.log(1); // 同步输出
    setTimeout(() => console.log(2), 0); // 宏任务
    Promise.resolve().then(() => console.log(3)); // 微任务
    console.log(4); // 同步输出
    // 输出顺序:1 → 4 → 3 → 2
    

五、DOM与事件

  1. 事件冒泡与捕获
  • 冒泡:事件从目标节点向上触发到根节点。
  • 捕获:事件从根节点向下触发到目标节点。
  • 阻止冒泡:event.stopPropagation()
  1. 不冒泡的事件
  • focus, blur, mouseenter, mouseleave, scroll等。
  1. mouseEnter vs mouseOver
  • mouseenter:进入元素时触发(不冒泡)。
  • mouseOver:进入或离开子元素时都可能触发。
  1. DOM操作方法
  • 查询:document.querySelector(), getElementById()
  • 添加:appendChild(), insertBefore()
  • 删除:removeChild(), remove()
  • 修改:textContent, setAttribute()
  1. 事件委托实现
    // 传统事件委托
    document.body.addEventListener('click', function(e) {
      if (e.target.matches('.btn')) {
        console.log('按钮被点击');
      }
    });
    

六、数据操作与存储

  1. 深拷贝与浅拷贝
  • 浅拷贝:Object.assign(), Array.slice()
  • 深拷贝:
    function deepCopy(obj) {
      return JSON.parse(JSON.stringify(obj));
    }
    
  1. 数组去重方法
  • Setnew Set(arr)[...new Set(arr)]
  • filterarr.filter((item, index) => index === firstIndex(item))
  1. 解构赋值成功条件 需要右侧是可迭代对象(如数组、字符串、Map等):

    var [a, b] = { a: 1, b: 2 }; // 需要先转换为数组或类似结构
    
  2. 存储机制区别

  • cookie:小数据量,每次HTTP请求携带。
  • sessionStorage:会话期,浏览器关闭失效。
  • localStorage:持久化存储,跨会话。

七、高级特性与API

  1. Proxy能否监听内部变化 是的,通过配置set陷阱:

    const obj = new Proxy({}, {
      set(target, key, value) {
        console.log(`属性 ${key} 被修改为 ${value}`);
        target[key] = value;
        return true;
      }
    });
    

    vue实现原理

    // 创建被代理的目标对象
    const target = { value: 123 };
    
    // 定义代理处理器
    const handler = {
      get(target, prop, receiver) {
        console.log(`读取值: ${prop} => ${target[prop]}`);
        return Reflect.get(target, prop, receiver);
      },
      set(target, prop, value, receiver) {
        console.log(`设置值: ${prop}${target[prop]}${value}`);
        // 只有当新值与旧值不同时才更新
        if (value !== target[prop]) {
          target[prop] = value;
          // 这里可以触发数据绑定的更新逻辑
          updateRelatedProperties();
        }
        return Reflect.set(target, prop, value, receiver);
      }
    };
    
    // 创建代理实例
    const proxy = new Proxy(target, handler);
    
    function updateRelatedProperties() {
      console.log("相关属性已同步更新");
    }
    
    // 使用代理对象进行操作
    proxy.value = 456; // 输出:
    // 设置值: value 从 123 → 456
    // 相关属性已同步更新
    
    console.log(proxy.value); // 输出:
    // 读取值: value => 456
    // 456
    

    实现要点:

    1. 将基础数值包装在对象中 ({ value: 123 }),使 Proxy 能够拦截操作
    2. get 陷阱中添加数据读取时的日志/验证逻辑
    3. set 陷阱中添加数据修改时的校验和联动更新逻辑
    4. 使用 Reflect 维持原有的继承链和默认行为
    5. 通过比较新旧值避免不必要的更新

    典型应用场景:

    • 双向数据绑定(Vue/React 等框架的核心机制)
    • 数据校验和格式化
    • 实时统计(如页面访问计数器)
    • 版本控制历史记录
  2. 防抖与节流实现

  • 防抖(Debounce):
    function debounce(func, delay) {
      let timer;
      return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => func.apply(this, args), delay);
      };
    }
    
  • 节流(Throttle):
    function throttle(func, delay) {
      let lastTime = 0;
      return function(...args) {
        if (Date.now() - lastTime >= delay) {
          func.apply(this, args);
          lastTime = Date.now();
        }
      };
    }
    
  1. callapplybind区别
  • call(obj, arg1, arg2):立即执行,参数列表。
  • apply(obj, [arg1, arg2]):立即执行,参数数组。
  • bind(obj, arg1, arg2):返回新函数,延迟执行。

八、内存与性能

  1. 垃圾回收机制
  • 标记清除(Mark and Sweep):
    1. 标记活动对象。
    2. 回收未标记对象的内存。
  1. 内存泄漏场景
  • 未解除事件监听(如DOM元素删除后未移除)。
  • 全局变量或闭包中未释放的大对象。
  • 循环引用(如父子节点互相引用)。

九、ES6+新特性

  1. ES6新增特性
  • let/const、箭头函数、模板字符串、解构赋值、类、模块化等。
  1. letconst作用域差异
  • let:块作用域,可重新赋值。
  • const:块作用域,不可修改(但数组/对象内容可变)。

十、其他

  1. 事件传播机制
  • 冒泡阶段:事件从目标节点向上触发。
  • 捕获阶段:事件从根节点向下触发。
  • 代理机制:通过事件委托优化性能。
  1. URL参数解析函数
    function parseQueryString(url) {
      const params = new URLSearchParams(new URL(url).search);
      return Object.fromEntries(params.entries());
    }