今日前端面试知识点总结(中型公司)

144 阅读17分钟

CSS 相关知识点详解

一、CSS 盒子模型

CSS 盒子模型是理解页面布局的关键。每个元素都被视为一个矩形盒子,包含以下部分:

  1. 内容区域(Content) :盒子的核心内容。
  2. 内边距(Padding) :内容与边框之间的空间。
  3. 边框(Border) :围绕内边距的边界。
  4. 外边距(Margin) :边框与其他元素之间的空白。
div {
  width: 200px; /* 内容宽度 */
  padding: 20px; /* 内边距 */
  border: 1px solid black; /* 边框 */
  margin: 10px; /* 外边距 */
}

盒子模型的尺寸计算

总宽度 = 左外边距 + 左边框 + 左内边距 + 宽度 + 右内边距 + 右边框 + 右外边距

总高度 = 上外边距 + 上边框 + 上内边距 + 高度 + 下内边距 + 下边框 + 下外边距

box-sizing 属性

box-sizing 属性定义了元素的总宽度和高度是否包括内边距和边框。

  • content-box:默认值,宽度和高度仅指内容区域。
  • border-box:宽度和高度包括内容、内边距和边框。

二、CSS 选择器的优先级

选择器的优先级决定了当多个样式规则应用于同一元素时,哪个规则会生效。CSS 选择器的优先级从高到低依次如下:

  1. 内联样式(Inline Styles) :直接在 HTML 元素的 style 属性中定义的样式。
  2. ID 选择器(ID Selectors) :通过 #id 定义的样式。
  3. 类选择器、属性选择器、伪类选择器(Class, Attribute, and Pseudo-class Selectors) :通过 .class[attr]:hover 等定义的样式。
  4. 元素选择器、伪元素选择器(Element and Pseudo-element Selectors) :通过 div::before 等定义的样式。
  5. 通用选择器(Universal Selector) :通过 * 定义的样式,优先级最低。

二、优先级计算规则

为了更精确地计算选择器的优先级,CSS 规范引入了一种基于权重的计算方法。具体规则如下:

  • 内联样式:权重为 1000
  • ID 选择器:每个 ID 提供 100 的权重。
  • 类选择器、属性选择器、伪类选择器:每个提供 10 的权重。
  • 元素选择器、伪元素选择器:每个提供 1 的权重。
  • 通用选择器:权重为 0

三、伪类与伪元素

  • 伪类:用于在元素处于特定状态时应用样式,如 :hover:active:focus
  • 伪元素:用于选择元素的特定部分,如 ::before::after::first-line

常见伪类

伪类描述示例
:hover鼠标悬停在元素上时a:hover
:active元素被激活(点击)时button:active
:focus元素获得焦点时input:focus
:first-child元素是父元素的第一个子元素时li:first-child
:last-child元素是父元素的最后一个子元素时li:last-child
:nth-child(n)元素是父元素的第 n 个子元素时tr:nth-child(odd)

常见伪元素

伪元素描述示例
::before在元素内容前插入内容div::before
::after在元素内容后插入内容div::after
::first-line元素的第一行文本p::first-line
::first-letter元素的第一个字母p::first-letter

示例

a:hover {
  color: blue; /* 鼠标悬停时的样式 */
}

p::first-line {
  font-weight: bold; /* 段落首行加粗 */
}

div::before {
  content: "► ";
  color: red;
}

四、隐藏元素的方法与区别

  1. display: none:元素完全从文档流中移除,不占用空间。
  2. visibility: hidden:元素不可见,但仍然占用空间。
  3. opacity: 0:元素透明度为 0,仍占据空间且可响应事件。

示例

.hidden-display {
  display: none;
}

.hidden-visibility {
  visibility: hidden;
}

.transparent {
  opacity: 0;
}

区别总结

方法是否占据空间是否响应事件使用场景
display: none完全移除元素,不希望占据空间
visibility: hidden需要保留空间,仅隐藏内容
opacity: 0需要透明效果,但仍可交互

五、水平垂直居中的方法

1. 使用 Flexbox

Flexbox 是现代 CSS 布局中最常用的居中方法,支持水平和垂直居中。

.container {
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
  height: 100vh; /* 确保容器高度占满视口 */
}

2. 使用 Grid 布局

CSS Grid 是强大的布局工具,也支持简单的居中操作。

.container {
  display: grid;
  place-items: center; /* 水平垂直居中 */
  height: 100vh; /* 确保容器高度占满视口 */
}

3. 绝对定位 + 负边距

传统方法,适用于需要兼容旧浏览器的场景。

.centered {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* 负边距值应为元素宽度和高度的一半 */
}

4. 表格布局

利用表格的垂直居中特性。

.container {
  display: table-cell;
  text-align: center; /* 水平居中 */
  vertical-align: middle; /* 垂直居中 */
  height: 100vh; /* 确保容器高度占满视口 */
}

JavaScript 相关知识点详解

一、ES6 变量定义

ES6 引入了 letconst,提供了更灵活和安全的变量定义方式,弥补了 var 的不足。

1. var 的局限性

  • 函数级作用域:在函数内部定义的变量在函数外部无法访问。
  • 变量提升:变量声明会被提升到当前作用域的顶部,导致一些意外行为。
  • 无法块级作用域:在 {} 中定义的变量会泄露到外部作用域。
if (true) {
  var message = "Hello";
}
console.log(message); // 输出 "Hello",变量泄露到全局作用域

2. let 和 const

  • 块级作用域:在 {} 中定义的变量仅在块内有效。
  • 不提升:必须在声明后才能使用。
  • const 不可重新赋值:声明后值不可更改(对象属性除外)。
if (true) {
  let message = "Hello";
  const pi = 3.14;
}
// console.log(message); // 报错,message 不在作用域内
// console.log(pi); // 报错,pi 不在作用域内

3. 使用建议

  • 使用 let 定义需要重新赋值的变量。
  • 使用 const 定义不需要重新赋值的变量(如 API 地址、配置项等)。
  • 避免使用 var,以防止意外的变量提升和作用域问题。

二、数组和对象遍历

数组遍历

  1. for 循环

最传统的遍历方式,适用于需要精确控制索引的场景。

const array = [1, 2, 3];
for (let i = 0; i < array.length; i++) {
  console.log(array[i]);
}
  1. forEach

数组实例方法,适用于简单的遍历操作,无法返回新数组。

array.forEach((item, index) => {
  console.log(`Item ${index}: ${item}`);
});
  1. map

返回新数组,适用于对数组元素进行映射操作。

const doubled = array.map(item => item * 2);
console.log(doubled); // [2, 4, 6]
  1. for...of

ES6 新增的语法,简洁且易读,适用于遍历数组值。

for (const item of array) {
  console.log(item);
}

对象遍历

  1. for...in

遍历对象自身的和继承的可枚举属性。

const obj = { a: 1, b: 2 };
for (const key in obj) {
  if (obj.hasOwnProperty(key)) {
    console.log(`${key}: ${obj[key]}`);
  }
}
  1. Object.keys

返回对象自身的可枚举属性的键数组。

Object.keys(obj).forEach(key => {
  console.log(`${key}: ${obj[key]}`);
});
  1. Object.valuesObject.entries

Object.values 返回属性值数组,Object.entries 返回键值对数组。

console.log(Object.values(obj)); // [1, 2]
Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});

三、事件冒泡与捕获

事件流

事件在 DOM 树中的传播过程包括三个阶段:

  1. 捕获阶段:事件从文档节点向下传播到目标节点。
  2. 目标阶段:事件到达目标节点。
  3. 冒泡阶段:事件从目标节点向上传播到文档节点。

事件监听器的添加

使用 addEventListener 方法可以指定事件监听器所在的阶段。

element.addEventListener('click', handler, useCapture);
  • useCapture 参数为 true 时,监听器在捕获阶段触发。
  • useCapture 参数为 false(默认值)时,监听器在冒泡阶段触发。

实际开发中的应用

  1. 事件委托

利用事件冒泡,在父元素上监听事件,减少事件监听器的数量。

document.getElementById('list').addEventListener('click', function(e) {
  if (e.target.tagName === 'LI') {
    console.log('Clicked: ', e.target.textContent);
  }
});
  1. 阻止事件传播

使用 stopPropagation 方法可以阻止事件进一步传播。

element.addEventListener('click', function(e) {
  e.stopPropagation();
  // 事件处理逻辑
});

四、Promise

Promise 是异步编程的一种解决方案,表示一个异步操作的最终完成或失败。它有三种状态:pending(进行中)、fulfilled(已完成)和 rejected(已失败)。

基本用法

const promise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 操作成功 */) {
    resolve(result);
  } else {
    reject(error);
  }
});

promise.then(result => {
  // 处理成功结果
}).catch(error => {
  // 处理错误
});

Promise 的三种状态

  1. pending:初始状态,既不是成功,也不是失败状态。
  2. fulfilled:操作成功完成,状态变为 fulfilled,会调用 .then 方法中指定的回调函数。
  3. rejected:操作失败,状态变为 rejected,会调用 .catch 方法中指定的回调函数。

常用方法

  • .then(onFulfilled, onRejected) :注册 fulfilled 和 rejected 状态的回调函数。
  • .catch(onRejected) :捕获 rejected 状态或执行过程中抛出的异常。
  • .finally(callback) :无论 Promise 最终状态如何,都会执行的回调函数。

静态方法

  • Promise.all(iterable) :等待所有 promise 解决,返回一个新的 promise。
  • Promise.race(iterable) :返回第一个解决的 promise 的结果。
  • Promise.resolve(value) :直接返回一个状态为 resolved 的 promise。
  • Promise.reject(reason) :直接返回一个状态为 rejected 的 promise。

示例

// 创建一个 promise
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功');
  }, 1000);
});

// 使用 .then 和 .catch
promise.then(result => console.log(result))
       .catch(error => console.error(error));

// 使用 async/await
async function fetchData() {
  try {
    const result = await promise;
    console.log(result);
  } catch (error) {
    console.error(error);
  }
}

fetchData();

浏览器与网络

同源策略与跨域

同源策略

同源策略是一种安全机制,用于限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。同源需要满足以下三个方面:

  • 协议相同
  • 域名相同
  • 端口相同

例如,http://www.a.comhttp://a.comhttp://a.eduhttps://a.com 等都被视为不同源。

跨域问题

当尝试访问不同源的资源时,浏览器出于安全考虑会限制某些操作,如:

  • Cookie、LocalStorage 和 IndexedDB 无法读取
  • DOM 无法获取
  • AJAX 请求不能发送

解决跨域的方法

CORS(跨域资源共享)

CORS 是一种 HTTP 机制,通过额外的 HTTP 响应头来告诉浏览器允许跨域请求。服务器需要在响应头中添加如 Access-Control-Allow-Origin 等信息。

代理服务器

通过设置一个代理服务器,将跨域请求代理到相同源的服务器上。客户端发起请求到代理服务器,代理服务器向目标服务器发起请求,并将结果返回给客户端。

JSONP

利用 <script> 标签的跨域能力实现跨域数据的访问。服务端返回动态生成的 JavaScript 脚本,客户端通过回调函数处理数据。

HTTP 状态码

HTTP 状态码用于表示服务器对请求的响应状态,常见的状态码分类如下:

成功响应(2xx)

  • 200 OK:请求成功,资源已被提取并在消息正文中传输。
  • 201 Created:请求已成功,并创建了一个新的资源。
  • 204 No Content:请求成功,但没有内容可发送。

重定向(3xx)

  • 301 Moved Permanently:被请求的资源已永久移动到新位置。
  • 302 Found:请求的资源临时从不同的 URI 响应请求。
  • 304 Not Modified:请求的资源未修改,客户端可继续使用本地缓存。

客户端错误(4xx)

  • 400 Bad Request:服务器无法理解请求的格式。
  • 403 Forbidden:服务器理解请求,但拒绝执行。
  • 404 Not Found:请求的资源不存在。

服务器错误(5xx)

  • 500 Internal Server Error:服务器遇到意外情况。
  • 502 Bad Gateway:服务器作为网关或代理,从上游服务器收到无效响应。
  • 503 Service Unavailable:服务器当前无法处理请求。
状态码描述常见用途
200 OK请求成功通用成功响应
201 Created资源已创建创建新资源后返回
204 No Content没有内容无需返回数据时
301 Moved Permanently永久重定向资源已永久移动
302 Found临时重定向资源临时移动
304 Not Modified未修改资源未更改,使用缓存
400 Bad Request错误请求客户端请求有误
403 Forbidden禁止访问无权限访问
404 Not Found未找到请求的资源不存在
500 Internal Server Error服务器内部错误服务器出现问题
502 Bad Gateway网关错误服务器作为网关时的错误
503 Service Unavailable服务不可用服务器暂时过载或维护

前端存储机制与 Vue.js 相关知识点详解

一、前端存储机制

1. localStorage、sessionStorage 与 Cookies 的区别

特性CookieslocalStoragesessionStorage
数据持久性会话结束或过期永久(除非清除)会话结束
数据量4KB 左右5MB 左右5MB 左右
与服务器通信自动发送不会自动发送不会自动发送
生命周期可设置过期时间直到清除浏览器会话结束
作用域所有窗口共享所有窗口共享同一窗口
安全性相对较低较高较高

2. 使用场景

  • Cookies:适用于需要将数据发送到服务器的场景,如用户登录状态的维持。
  • localStorage:适用于需要长期存储数据的场景,如用户偏好设置。
  • sessionStorage:适用于仅在会话期间存储数据的场景,如购物车临时数据。

二、Vue.js 相关

1. 生命周期钩子

Vue 组件的生命周期包括多个阶段,常用钩子有:

  • beforeCreate:实例初始化后,数据观测和事件配置前。
  • created:实例创建完成,数据观测和事件配置完成。
  • beforeMount:挂载开始之前。
  • mounted:挂载完成,DOM 渲染完成。
  • beforeUpdate:数据更新时,在 DOM 更新之前。
  • updated:DOM 更新完成。
  • beforeUnmount:实例销毁前,仍可访问数据和方法。
  • unmounted:实例销毁后,解绑所有事件监听器。

2. 组件通信

在 Vue 中,父子组件通信主要通过以下方式:

  • 父传子:通过 props 传递数据。
  • 子传父:通过 $emit 触发事件,父组件监听事件。
<!-- 父组件 -->
<child-component :message="parentMessage" @child-event="handleChildEvent"></child-component>
// 子组件
this.$emit('child-event', data);

3. 插槽

插槽是 Vue 的内容分发机制,允许父组件向子组件传递自定义内容。

  • 默认插槽
<child-component>
  <p>这是默认插槽内容</p>
</child-component>
  • 具名插槽
<child-component>
  <template v-slot:header>
    <h1>标题</h1>
  </template>
</child-component>
  • 作用域插槽:子组件向父组件传递数据。
<child-component>
  <template v-slot:item="slotProps">
    <li>{{ slotProps.item.text }}</li>
  </template>
</child-component>

4. 动态路由

在 Vue 项目中,动态路由常用于实现权限控制、路由懒加载等。

// 动态路由示例
const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      {
        path: 'profile',
        component: () => import('./Profile.vue') // 懒加载
      }
    ]
  }
];

前端工具与实践详解

一、常见的 Git 指令

Git 是一个开源的分布式版本控制系统,用于管理项目代码的版本历史记录。以下是一些常用的 Git 指令:

1. 仓库初始化与克隆

  • git init:在当前目录初始化一个空的 Git 仓库。
  • git clone <repository-url> :克隆一个已有的远程仓库到本地。
git clone https://github.com/username/repository.git

2. 日常操作

  • git add <file> :将指定文件添加到暂存区。
  • git add . :将所有修改过的文件添加到暂存区。
  • git commit -m "message" :提交暂存区的文件,添加提交信息。
  • git status:查看当前仓库的状态,包括哪些文件已修改但未暂存,哪些文件已暂存。
  • git log:查看历史提交记录。
git add .
git commit -m "添加新功能"
git status
git log

3. 分支管理

  • git branch:列出所有本地分支。
  • git branch <branch-name> :创建一个新分支。
  • git checkout <branch-name> :切换到指定分支。
  • git merge <branch-name> :将指定分支合并到当前分支。
  • git rebase <branch-name> :将当前分支的提交重新应用到指定分支上。
git branch
git branch feature-login
git checkout feature-login
git merge feature-login
git rebase feature-login

4. 远程仓库同步

  • git remote add <name> <url> :添加远程仓库。
  • git fetch <remote-name> :从远程仓库获取最新的分支和提交信息。
  • git pull <remote-name> <branch-name> :从远程仓库拉取指定分支的最新代码并合并到当前分支。
  • git push <remote-name> <branch-name> :将本地指定分支的代码推送到远程仓库。
git remote add origin https://github.com/username/repository.git
git fetch origin
git pull origin main
git push origin feature-login

5. 版本回退

  • git reset --hard <commit-hash> :将当前分支的指针重置到指定的提交,丢弃该提交之后的所有更改。
  • git reset --soft <commit-hash> :将当前分支的指针重置到指定的提交,但保留工作区和暂存区的更改。
git reset --hard a1b2c3d
git reset --soft a1b2c3d

二、对前端工程化的理解

前端工程化是通过工具和流程提升开发效率、代码质量和可维护性的实践。它包括以下几个方面:

1. 模块化开发

将代码分为独立模块,便于复用和管理。常见的模块化规范有 CommonJS、AMD、ES6 模块等。

2. 自动化构建

使用工具如 Webpack、Rollup 自动化打包代码,处理如压缩、丑化、代码分割等任务。

3. 持续集成/持续部署(CI/CD)

通过自动化测试和部署流程,确保代码质量和快速发布。常用的 CI/CD 工具有 Jenkins、Travis CI、GitHub Actions 等。

# GitHub Actions 示例
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Install dependencies
        run: npm install
      - name: Build project
        run: npm run build
      - name: Run tests
        run: npm test

4. 代码规范

统一团队代码风格,使用 ESLint、Prettier 等工具进行代码检查和格式化。

// .eslintrc 示例
module.exports = {
  env: {
    browser: true,
    es6: true
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended'
  ],
  rules: {
    'no-console': 'off',
    'react/prop-types': 'off'
  }
};

5. 单元测试与集成测试

使用测试框架如 Jest、Mocha 进行单元测试和集成测试,确保代码的正确性和稳定性。

// Jest 单元测试示例
// math.test.js
const { add } = require('./math');

test('add function', () => {
  expect(add(1, 2)).toBe(3);
});

三、Less 与 Sass 预处理器

Less 和 Sass 是两种流行的 CSS 预处理器,它们提供了变量、嵌套规则、混合等特性,使 CSS 更加灵活和可维护。

Less

// 定义变量
@primary-color: #1DA57A;

// 嵌套规则
.container {
  color: @primary-color;
  .header {
    height: 60px;
  }
}

// 混合
.rounded-corners (@radius: 5px) {
  border-radius: @radius;
}

.box {
  .rounded-corners(8px);
}

Sass

// 定义变量
$primary-color: #1DA57A;

// 嵌套规则
.container {
  color: $primary-color;
  .header {
    height: 60px;
  }
}

// 混合
@mixin rounded-corners ($radius: 5px) {
  border-radius: $radius;
}

.box {
  @include rounded-corners(8px);
}

四、响应式布局

响应式布局确保页面在不同设备上都能良好显示。

1. 媒体查询

通过媒体查询,可以针对不同屏幕尺寸定义不同的样式。

/* 针对小屏幕 */
@media (max-width: 768px) {
  .container {
    padding: 10px;
  }
}

/* 针对大屏幕 */
@media (min-width: 769px) {
  .container {
    padding: 20px;
  }
}

2. 弹性布局(Flexbox)

Flexbox 是一种现代的布局方式,可以轻松实现复杂的布局。

.container {
  display: flex;
  justify-content: space-between; /* 水平分布 */
  align-items: center; /* 垂直居中 */
}

.item {
  flex: 1;
  margin: 0 10px;
}

3. 网格布局(Grid)

CSS Grid 是强大的布局工具,适用于二维布局。

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr); /* 分成三列 */
  grid-gap: 20px; /* 单元格间距 */
}

.item {
  background: #f0f0f0;
  padding: 20px;
}

4. 图片响应式

确保图片在不同设备上都能正确显示。

img {
  max-width: 100%;
  height: auto;
}

五、el-table 组件的二次封装

在实际项目中,我们经常需要对一些常用的组件进行二次封装,以提高代码的复用性和可维护性。以下是对 Element UI 中 el-table 组件的二次封装示例。

1. 定义封装组件

<!-- MyTable.vue -->
<template>
  <el-table
    :data="data"
    :border="border"
    :stripe="stripe"
    :height="height"
    @selection-change="handleSelectionChange"
    ref="table"
  >
    <el-table-column
      v-if="selection"
      type="selection"
      width="55"
    ></el-table-column>
    <slot></slot>
  </el-table>
</template>

<script>
export default {
  name: 'MyTable',
  props: {
    data: {
      type: Array,
      default: () => []
    },
    border: {
      type: Boolean,
      default: true
    },
    stripe: {
      type: Boolean,
      default: true
    },
    height: {
      type: [String, Number],
      default: 'auto'
    },
    selection: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    handleSelectionChange(selection) {
      this.$emit('selection-change', selection);
    },
    clearSelection() {
      this.$refs.table.clearSelection();
    },
    toggleRowSelection(row, selected) {
      this.$refs.table.toggleRowSelection(row, selected);
    }
  }
};
</script>

<style scoped>
/* 自定义样式 */
</style>

2. 使用封装组件

<!-- UserList.vue -->
<template>
  <div>
    <my-table
      :data="users"
      :border="true"
      :stripe="true"
      :height="400"
      :selection="true"
      @selection-change="handleSelectionChange"
    >
      <el-table-column
        prop="name"
        label="姓名"
        width="180"
      ></el-table-column>
      <el-table-column
        prop="age"
        label="年龄"
        width="100"
      ></el-table-column>
      <el-table-column
        prop="email"
        label="邮箱"
      ></el-table-column>
    </my-table>
  </div>
</template>

<script>
import MyTable from './MyTable.vue';

export default {
  components: {
    MyTable
  },
  data() {
    return {
      users: [
        { name: '张三', age: 25, email: 'zhangsan@example.com' },
        { name: '李四', age: 30, email: 'lisi@example.com' },
        { name: '王五', age: 28, email: 'wangwu@example.com' }
      ]
    };
  },
  methods: {
    handleSelectionChange(selection) {
      console.log('选中的行:', selection);
    }
  }
};
</script>

通过这种方式,我们对 el-table 组件进行了二次封装,增加了更多的功能和灵活性,同时保持了代码的简洁和可维护性。在实际项目中,可以根据具体需求进一步扩展和定制封装组件的功能。 }