面试版-前端开发核心知识

一、DOM操作与事件处理:用户交互的基石

1.1 原生点击事件的三种绑定方式

前端与用户的交互始于事件,而点击事件是最基础的交互方式。原生JavaScript绑定点击事件主要有以下三种方式:

(1)HTML内联绑定(不推荐)

直接在HTML标签中通过onclick属性定义事件逻辑:

javascript 复制代码
<button onclick="handleClick()">点击我</button>
<script>
  function handleClick() {
    console.log('内联事件触发');
  }
</script>

​缺点​​:HTML与JS逻辑耦合,无法动态修改事件,维护成本高。

(2)DOM属性绑定(单事件)

通过JS获取元素后,直接为onclick属性赋值:

javascript 复制代码
const btn = document.getElementById('btn');
btn.onclick = function() {
  console.log('DOM属性事件触发');
};

​特点​​:只能绑定一个同类型事件(后绑定的会覆盖前一个),适合简单场景。

(3)addEventListener(推荐)

通过addEventListener方法绑定事件,支持多事件监听、事件冒泡/捕获控制:

javascript 复制代码
const btn = document.getElementById('btn');
// 第三个参数:useCapture(布尔值,默认false=冒泡阶段)
btn.addEventListener('click', function(e) {
  console.log('冒泡阶段触发');
}, false);

// 捕获阶段绑定(需设置为true)
document.body.addEventListener('click', function(e) {
  console.log('捕获阶段触发');
}, true);

​优势​​:

  • 支持同一元素绑定多个同类型事件(按注册顺序执行);
  • 可通过removeEventListener精准解绑;
  • 支持事件流控制(冒泡/捕获)。

1.2 如何获取DOM元素?

要操作DOM元素,首先需要获取其引用。以下是8种常用方法:

方法 说明 示例
getElementById() 根据ID获取单个元素(唯一) document.getElementById('box')
querySelector() CSS选择器获取​​首个匹配​​元素 document.querySelector('.btn')
querySelectorAll() CSS选择器获取​​所有匹配​​元素(返回NodeList) document.querySelectorAll('li')
getElementsByClassName() 根据类名获取元素集合(HTMLCollection,动态更新) document.getElementsByClassName('item')
getElementsByTagName() 根据标签名获取元素集合(HTMLCollection,动态更新) document.getElementsByTagName('div')
getElementsByName() 根据name属性获取元素集合(NodeList,静态) document.getElementsByName('user')
document.body 直接获取<body>元素 const body = document.body
document.documentElement 直接获取<html>元素 const html = document.documentElement

1.3 事件传播机制:捕获→目标→冒泡

当点击一个元素时,事件会经历三个阶段:

  1. ​捕获阶段​ :事件从window开始,逐层向下传播到目标元素的父节点;
  2. ​目标阶段​:事件到达目标元素本身;
  3. ​冒泡阶段​ :事件从目标元素开始,逐层向上传播到window

​关键操作​​:

  • 阻止事件冒泡:e.stopPropagation()
  • 阻止默认行为(如链接跳转):e.preventDefault()
  • 停止事件传播(同时阻止冒泡和默认行为):e.stopImmediatePropagation()(仅适用于当前元素的其他监听器)。

​示例:阻止表单提交​

javascript 复制代码
const form = document.querySelector('form');
form.addEventListener('submit', function(e) {
  e.preventDefault(); // 阻止表单默认提交行为
  console.log('表单验证通过,异步提交');
});

二、数据存储:从Cookie到现代存储方案

2.1 Cookie的本质与局限

Cookie是服务器通过Set-Cookie响应头写入客户端的​​键值对数据​​,随每次HTTP请求自动携带到服务器。其核心特性如下:

特性 说明
存储容量 约4KB(不同浏览器略有差异)
生命周期 默认会话结束失效(可通过ExpiresMax-Age设置持久化)
作用域 Domain(主域+子域)和Path(路径)控制
安全性 明文传输(易被篡改),可通过HttpOnly(禁止JS访问)防XSS,Secure(仅HTTPS传输)
同步性 每次请求自动携带,可能影响性能

​典型场景​ ​:用户登录状态(如sessionId)、购物车临时数据。

2.2 现代存储方案对比

随着前端应用复杂度提升,Cookie的局限性逐渐显现,以下是更常用的替代方案:

方案 容量 生命周期 同步/异步 典型用途 API示例
localStorage 5-10MB 手动清除(永久有效) 同步 长期配置(主题、语言) localStorage.setItem('theme', 'dark')
sessionStorage 5-10MB 标签页关闭时失效 同步 临时会话数据 sessionStorage.removeItem('token')
IndexedDB 250MB+ 永久(需手动清理) 异步 大量结构化数据(如离线文档) const db = await indexedDB.open('myDB')
Cache API 无限制 手动控制(Service Worker管理) 异步 缓存静态资源(PWA) caches.open('v1').then(cache => cache.add('/img/logo.png'))

​选择建议​​:

  • 需长期保留的小量数据 → localStorage
  • 临时会话数据 → sessionStorage
  • 大量结构化数据或离线应用 → IndexedDB
  • 静态资源缓存 → Cache API(配合Service Worker)。

三、ES6+语法与异步编程:现代前端的基石

3.1 ES6核心语法速览

ES6(ECMAScript 2015)是前端语法的重要里程碑,以下是最常用特性:

(1)箭头函数(=>

简化函数写法,绑定词法作用域的this(无独立this):

javascript 复制代码
// 传统函数
const add = function(a, b) { return a + b; };

// 箭头函数(单表达式可省略大括号和return)
const add = (a, b) => a + b;

// 多表达式需大括号
const double = (num) => {
  if (num > 0) return num * 2;
  return num;
};
(2)解构赋值

快速提取对象/数组的值,简化变量声明:

javascript 复制代码
// 对象解构
const user = { name: 'Xback', age: 23 };
const { name, age } = user; // name='Xback', age=23

// 数组解构
const [first, second] = [1, 2]; // first=1, second=2

// 重命名
const { name: userName } = user; // userName='Xback'
(3)模板字符串(`````)

支持变量嵌入和多行字符串:

javascript 复制代码
const message = `用户${user.name}(${user.age}岁)登录成功`;
// 输出:"用户Xback(23岁)登录成功"
(4)类(class

面向对象的语法糖,替代原型链:

javascript 复制代码
class Person {
  constructor(name) {
    this.name = name;
  }
  sayHello() {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const alice = new Person('Xback');
alice.sayHello(); // "Hello, I'm Xback"

3.2 异步编程:从回调到async/await

前端应用中,网络请求、文件读写等操作均为异步,需避免阻塞主线程。ES6后的异步解决方案演进如下:

(1)回调地狱(Callback Hell)

早期通过回调函数处理异步,但多层嵌套会导致代码可读性差、错误难以捕获:

javascript 复制代码
fetch('/api1')
  .then(res => res.json())
  .then(data1 => {
    fetch(`/api2?id=${data1.id}`)
      .then(res => res.json())
      .then(data2 => {
        // 更多嵌套...
      });
  });
(2)Promise:链式调用

Promise通过then/catch实现线性链式调用,解决回调地狱:

javascript 复制代码
fetch('/api1')
  .then(res => res.json())
  .then(data1 => fetch(`/api2?id=${data1.id}`))
  .then(res => res.json())
  .then(data2 => console.log(data2))
  .catch(err => console.error('请求失败', err));
(3)async/await:同步写法

基于Promise的语法糖,用async标记函数,await等待Promise解决,代码更接近同步逻辑:

javascript 复制代码
async function fetchAllData() {
  try {
    const res1 = await fetch('/api1');
    const data1 = await res1.json();
    
    const res2 = await fetch(`/api2?id=${data1.id}`);
    const data2 = await res2.json();
    
    return [data1, data2];
  } catch (err) {
    console.error('请求失败', err);
  }
}

// 调用
fetchAllData().then(([data1, data2]) => {
  console.log('所有数据加载完成', data1, data2);
});
(4)并发请求:Promise.all

若需同时发起多个请求(如获取用户信息、商品列表),可用Promise.all并行执行,提升效率:

javascript 复制代码
async function fetchConcurrent() {
  const urls = ['/api/user', '/api/products', '/api/cart'];
  // 并行发起请求
  const promises = urls.map(url => fetch(url).then(res => res.json()));
  // 等待所有请求完成
  const [user, products, cart] = await Promise.all(promises);
  return { user, products, cart };
}

3.3 函数进阶:call/apply/bindProxy

(1)call/apply/bind:修改this指向

三者均可显式绑定函数的this,但参数传递和执行时机不同:

方法 参数形式 执行时机 返回值 典型场景
call 参数列表(逗号分隔) 立即执行 函数返回值 借用其他对象的方法
apply 参数数组 立即执行 函数返回值 传递数组参数(如Math.max
bind 参数列表(可选) 延迟执行 绑定后的新函数 事件回调固定this

​示例:​

javascript 复制代码
const obj = { x: 10 };
function sum(y) { return this.x + y; }

sum.call(obj, 5);    // 15(立即执行,参数列表)
sum.apply(obj, [5]); // 15(立即执行,参数数组)

const boundSum = sum.bind(obj, 5);
boundSum();          // 15(延迟执行,参数部分预设)
(2)Proxy:对象代理

Proxy用于创建一个对象的代理,拦截并自定义对象的基本操作(如属性读取、赋值),是实现响应式数据(如Vue 3)、数据校验等功能的利器:

javascript 复制代码
const target = { name: 'Alice', age: 25 };
const handler = {
  get(obj, prop) {
    if (prop in obj) {
      return obj[prop].toUpperCase(); // 读取时转换为大写
    }
    return '属性不存在';
  },
  set(obj, prop, value) {
    if (prop === 'age' && typeof value !== 'number') {
      throw new Error('年龄必须是数字');
    }
    obj[prop] = value; // 设置时校验类型
    return true; // 必须返回true表示设置成功
  }
};

const proxy = new Proxy(target, handler);
console.log(proxy.name); // "ALICE"(触发get)
proxy.age = '26';        // 抛出错误(类型校验失败)

四、性能优化与工程实践

4.1 图片懒加载:提升首屏加载速度

图片懒加载的核心思想是:​​仅当图片进入可视区域时再加载真实资源​​,减少首屏请求量。

实现步骤:
  1. ​占位符替换​ :将<img>src属性替换为data-src(存储真实URL),避免初始加载;
  2. ​视口监听​ :使用Intersection Observer API监听图片是否进入视口;
  3. ​资源加载​ :当图片进入视口时,将data-src赋值给src,触发加载。

​代码示例:​

javascript 复制代码
// 1. 获取所有带data-src的图片
const lazyImages = document.querySelectorAll('img[data-src]');

// 2. 创建Intersection Observer实例
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) { // 图片进入视口
      const img = entry.target;
      img.src = img.dataset.src; // 加载真实图片
      img.removeAttribute('data-src'); // 移除占位符属性
      observer.unobserve(img); // 停止监听已加载的图片
    }
  });
});

// 3. 开始监听所有图片
lazyImages.forEach(img => observer.observe(img));

​关键技术点​​:

  • Intersection Observer API:现代浏览器提供的视口监听接口,替代传统的滚动事件+getBoundingClientRect(性能更优);
  • data-*属性:HTML5自定义数据属性,用于存储额外信息。

4.2 内存泄漏:原因与解决方案

内存泄漏指程序中不再使用的内存未被释放,导致内存占用持续增长,最终可能引发页面卡顿或崩溃。常见原因及解决方法:

(1)未移除的事件监听

​原因​ ​:为元素添加事件监听后,若元素被移除但监听器未解绑,监听器仍会引用元素,导致其无法被垃圾回收。

​解决​ ​:在元素移除前调用removeEventListener解绑监听器,或使用once: true(仅触发一次后自动移除)。

(2)未释放的闭包

​原因​ ​:闭包会长期持有外部函数的变量引用,若闭包被全局变量引用,变量无法被回收。

​解决​ ​:避免不必要的闭包,或在闭包中使用weakMap/weakSet(弱引用,不阻止垃圾回收)。

(3)定时器未清除

​原因​ ​:setInterval未及时清除,导致回调函数持续引用上下文。

​解决​ ​:在组件卸载或不需要定时器时调用clearInterval/clearTimeout

(4)DOM引用残留

​原因​ ​:JS中保留了已移除DOM元素的引用(如cache对象),导致DOM无法被回收。

​解决​ ​:移除DOM后,同步清除JS中的引用(如delete cache[key])。

​检测工具​ ​:Chrome DevTools的Memory面板(记录堆快照,对比前后内存变化)。


五、前端工具链与生态

5.1 Git:版本控制的核心技能

Git是前端开发的必备工具,以下是常用命令及场景:

场景 命令示例 说明
克隆仓库 git clone https://github.com/xxx/repo.git 从远程仓库复制代码到本地
创建并切换分支 git checkout -b feature/new-module 新建feature/new-module分支并切换
添加修改 git add . 添加所有修改到暂存区
提交修改 git commit -m "feat: 新增用户模块" 提交暂存区的修改到本地仓库
推送分支到远程 git push origin feature/new-module 将本地分支推送到远程仓库
拉取远程更新 git pull origin main 拉取远程main分支并合并到本地
查看提交历史 git log --oneline 简洁查看提交记录
回退到指定版本 git reset --hard abc123 回退到哈希为abc123的提交(危险操作)

​可视化工具​​:VS Code内置Git面板、SourceTree(适合复杂操作,如合并冲突解决)。

5.2 调试技巧:定位问题的关键

调试是开发者的核心能力,浏览器开发者工具(DevTools)是最常用的工具。

(1)网络面板(Network)

​作用​​:监控所有网络请求,分析请求耗时、状态码、响应内容。

​关键信息​​:

  • ​状态码​200(成功)、404(资源不存在)、500(服务器错误);
  • ​请求头(Request Headers)​
    • User-Agent:浏览器/设备信息;
    • Cookie:当前请求携带的Cookie;
    • Content-Type:请求体数据类型(如application/json);
  • ​响应头(Response Headers)​
    • Cache-Control:缓存策略(如max-age=3600);
    • Set-Cookie:服务器设置的Cookie;
  • ​Timing​:请求各阶段耗时(DNS查询、TCP连接、TTFB、下载)。
(2)元素面板(Elements)

​作用​​:查看和修改DOM结构、CSS样式,调试布局问题。

​常用功能​​:

  • 选择DOM元素(左上角箭头图标);
  • 实时编辑CSS(Styles面板);
  • 查看盒模型(Computed面板);
  • 断点调试(Event Listeners面板添加事件断点)。

六、框架与生态:从Vue到Uniapp

6.1 Uniapp与Vue的区别

Uniapp是基于Vue的跨端开发框架,目标是"一套代码,多端运行"(小程序、H5、App)。与标准Vue的主要差异:

特性 Uniapp 标准Vue(Web)
运行环境 小程序引擎(如微信JSCore)、H5、App 浏览器(JS引擎)
模板语法 支持Vue模板,但部分指令受限(如v-html 完整Vue模板语法
组件库 需使用Uniapp组件(如<uni-list> 可使用任意第三方Vue组件库
跨端适配 自动处理不同端的API差异(如uni.request 需手动适配不同浏览器
构建流程 基于vue-cli,但增加多端编译步骤 直接打包为浏览器可识别的代码

6.2 Element UI与TypeScript

Element UI是基于Vue的PC端组件库,而TypeScript(TS)是JavaScript的超集,添加了静态类型系统。

​TS的核心优势​​:

  • ​类型检查​:编译时捕获类型错误(如函数参数类型不匹配);
  • ​代码提示​:IDE(如VS Code)可自动推断变量类型,提升开发效率;
  • ​重构安全​:修改变量名或类型时,TS会提示所有受影响的代码位置。

​示例:TS定义组件Props​

javascript 复制代码
import { defineComponent, PropType } from 'vue';

export default defineComponent({
  props: {
    // 定义用户对象(必传)
    user: {
      type: Object as PropType<{ name: string; age: number }>,
      required: true
    },
    // 定义可选数字(默认值10)
    count: {
      type: Number,
      default: 10
    }
  },
  setup(props) {
    console.log(props.user.name); // 类型推断:string
  }
});
相关推荐
IT_10249 分钟前
Spring Boot项目开发实战销售管理系统——数据库设计!
java·开发语言·数据库·spring boot·后端·oracle
墨菲安全30 分钟前
NPM组件 betsson 等窃取主机敏感信息
前端·npm·node.js·软件供应链安全·主机信息窃取·npm组件投毒
GISer_Jing31 分钟前
Monorepo+Pnpm+Turborepo
前端·javascript·ecmascript
天涯学馆31 分钟前
前端开发也能用 WebAssembly?这些场景超实用!
前端·javascript·面试
new_zhou42 分钟前
Windows qt打包编译好的程序
开发语言·windows·qt·打包程序
ye9044 分钟前
银河麒麟V10服务器版 + openGuass + JDK +Tomcat
java·开发语言·tomcat
武昌库里写JAVA1 小时前
Oracle如何使用序列 Oracle序列使用教程
java·开发语言·spring boot·学习·课程设计
showyoui2 小时前
Python 闭包(Closure)实战总结
开发语言·python
我在北京coding2 小时前
TypeError: Cannot read properties of undefined (reading ‘queryComponents‘)
前端·javascript·vue.js
今天背单词了吗9802 小时前
算法学习笔记:7.Dijkstra 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·开发语言·数据结构·笔记·算法