面试、笔试总结

北京掌上科技笔试(未过)

HTTP状态码

以下是HTTP状态码的主要类别及一些常见的状态码:

1xx:信息性状态码

  • 100 Continue:初始部分已收到,客户端可以继续发送请求的其余部分。
  • 101 Switching Protocols:服务器已理解客户端的请求,并将使用不同的协议来处理请求。

2xx:成功状态码

  • 200 OK:请求已成功处理。
  • 201 Created:请求已成功处理,并创建了新的资源。
  • 204 No Content:请求成功处理,但不返回内容。

3xx:重定向状态码

  • 301 Moved Permanently:请求的资源已永久移动到新位置。
  • 302 Found:请求的资源临时移动到另一个URL。
  • 304 Not Modified:请求的资源未改变,客户端可以使用缓存的版本。

4xx:客户端错误状态码

  • 400 Bad Request:服务器无法理解请求,可能是因为语法错误。
  • 401 Unauthorized:请求未授权,需要用户身份验证。
  • 403 Forbidden:服务器理解请求,但拒绝执行。
  • 404 Not Found:请求的资源未找到。

5xx:服务器错误状态码

  • 500 Internal Server Error:服务器遇到意外情况,无法完成请求。
  • 502 Bad Gateway:作为网关或代理的服务器收到无效响应。
  • 503 Service Unavailable:服务器暂时不可用,通常是过载或维护。

parseInt函数

parseInt 是一个用于将字符串解析为整数的 JavaScript 函数。它通常用于从文本输入中提取数字。

基本语法

1
parseInt(string, radix);
  • string: 必需。要被解析的字符串。
  • radix: 可选。一个介于 2 和 36 之间的整数,表示基数(在其他进制下表示数字的地方)。例如,10 表示十进制,16 表示十六进制。

返回值

如果字符串开头是一个有效的数字,则返回该数字(以整数形式);如果解析失败,则返回 NaN(Not-a-Number)。

使用示例

1
2
3
4
5
6
7
8
console.log(parseInt("10"));          // 10
console.log(parseInt("10.5")); // 10
console.log(parseInt("abc")); // NaN
console.log(parseInt("10 years old")); // 10
console.log(parseInt("10", 10)); // 10
console.log(parseInt("FF", 16)); // 255
console.log(parseInt("1001", 2)); // 9
console.log(parseInt(" 20 ")); // 20 (忽略前后的空白)

注意事项

  1. 自动忽略前导空格: parseInt 会忽略字符串开头的所有空格。
  2. 非数字字符的处理: parseInt 只会解析字符串中最开始的数字字符,如果遇到第一个非数字字符就停止解析。
  3. radix 的重要性: 如果不指定 radix,可能会导致解析结果不如预期。特别是当字符串以 0x 开头时,会被认为是十六进制,或者以 0 开头时则被认为是八进制(在某些旧浏览器中)。因此,推荐总是指定 radix

示例中对 radix 的影响

1
2
3
4
5
console.log(parseInt("8"));          // 8 (默认 radix = 10)
console.log(parseInt("08")); // 8 (默认 radix = 10)
console.log(parseInt("010")); // 10 (默认 radix = 10)
console.log(parseInt("010", 8)); // 8 (手动指定为八进制)
console.log(parseInt("0x10")); // 16 (十六进制)

使用 parseInt 时,确保字符串包含有效的数字和选定的基数,以避免潜在的解析错误。

除以0的规则

在 JavaScript 中,除以零的运算遵循 IEEE 754 浮点数标准,具体规则如下:

  • 任意正数 除以 的结果是 正无穷大 (Infinity)。
  • 任意负数 除以 的结果是 负无穷大 (-Infinity)。
  • 除以 的结果是 NaN(Not-a-Number)。

如何创建一个BFC

什么是BFC:BFC(Block Formatting Context)是一个独立的渲染区域,块级元素在这个区域中进行独立的布局,形成一个隔离的环境。BFC 用于控制元素的布局和内容流动。它有助于解决一些常见的布局问题,比如清除浮动、避免元素重叠等。

如何创建BFC

在 CSS 中,以下条件会创建 BFC:

  1. 根元素<html> 元素。
  2. 浮动元素:任何 CSS float 属性值不为 none 的元素。
  3. 绝对定位元素:任何 CSS position 属性值为 absolutefixed 的元素。
  4. 内联块元素:应用了 display: flow-root; 的元素。
  5. 表格元素:如 <table><table-cell><table-caption> 等。
  6. overflow 属性:应用了 overflow 属性且值不为 visible 的任何元素(如 overflow: auto;overflow: hidden;)。

Math函数

1. Math.PI

问题: Math.PI 的值是多少?它适用于什么情况?

答案:

  • Math.PI 的值约为 3.141592653589793
  • 它常用于圆的计算,比如周长和面积。公式包括:
    • 周长: C = 2 * Math.PI * r
    • 面积: A = Math.PI * r^2

2. Math.random()

问题: Math.random() 方法返回什么?如何生成一个范围在 [0, 10) 的随机整数?

答案:

  • Math.random() 方法返回一个介于 0(包含)和 1(不包含)之间的伪随机数。

  • 要生成一个范围在 [0, 10) 的随机整数,可以使用以下代码:

    1
    2
    3
    4
    5
    //生成一个0 - x 的随机数 ===> Math.floor(Math.random() * (x + 1) )
    console.log(Math.floor(Math.random() * 6 )); // 0 - 5

    //生成一个a,b之间的随机数 ====> Math.floor(Math.random() * (b - a) + a)
    console.log(Math.floor(Math.random()*(10-2)+2)); //生成 2 ~ 9

3. Math.floor() 和 Math.ceil()

问题: Math.floor()Math.ceil() 有何区别?请给出示例。

答案:

  • Math.floor() 返回小于或等于给定数字的最大整数(向下取整)。
  • Math.ceil() 返回大于或等于给定数字的最小整数(向上取整)。

示例:

1
2
javascript复制代码console.log(Math.floor(3.7)); // 输出: 3
console.log(Math.ceil(3.7)); // 输出: 4

4. Math.pow()

问题: Math.pow(x, y) 的作用是什么?如果 x = 2y = 3,此函数会返回什么?

答案:

  • Math.pow(x, y) 返回 xy 次幂。
  • 如果 x = 2y = 3,返回值是 2^3 = 8

5. Math.abs()

问题: Math.abs(-5) 的输出是什么?Math.abs(5) 呢?

答案:

  • Math.abs() 返回数字的绝对值。
  • Math.abs(-5) 的输出为 5Math.abs(5) 的输出为 5

6. Math.max() 和 Math.min()

问题: Math.max(1, 5, 3, 9, -1)Math.min(1, 5, 3, 9, -1) 的结果分别是什么?

答案:

  • Math.max(1, 5, 3, 9, -1) 的结果是 9,这是最大的数。
  • Math.min(1, 5, 3, 9, -1) 的结果是 -1,这是最小的数。

7. Math.sqrt()

问题: Math.sqrt(16) 的返回值是什么?Math.sqrt(-4) 呢?

答案:

  • Math.sqrt(16) 的返回值是 4
  • Math.sqrt(-4) 会返回 NaN (Not-a-Number),因为负数没有实数平方根。

8. Math.round()

问题: Math.round(4.5)Math.round(4.4) 的输出分别是什么?

答案:

  • Math.round(4.5) 的输出是 5(四舍五入)。
  • Math.round(4.4) 的输出是 4

9. Math.sign()

问题: Math.sign(-10)Math.sign(0) 的返回值分别是什么?

答案:

  • Math.sign(-10) 返回 -1(表示负数)。
  • Math.sign(0) 返回 0(看作零)。

10. Math.trunc()

问题: Math.trunc(4.9)Math.trunc(-4.9) 分别会返回什么?

答案:

  • Math.trunc(4.9) 返回 4(去掉小数部分)。
  • Math.trunc(-4.9) 返回 -4(去掉小数部分)。

sort函数

sort() 方法默认将数组元素转为字符串,然后按照 Unicode 编码进行排序。这会在对数字排序时造成问题,因为数字会被转换为字符串进行比较。

例子:字符串排序

1
2
3
const fruits = ['banana', 'apple', 'cherry'];
fruits.sort();
console.log(fruits); // 输出: ['apple', 'banana', 'cherry']

例子:数字排序

在对数字进行排序时,默认的字符串排序方法可能会产生不正确的结果:

1
2
3
const numbers = [10, 2, 1, 21];
numbers.sort();
console.log(numbers); // 输出: [1, 10, 2, 21],并不是我们预期的 [1, 2, 10, 21]

ES5、ES6类的概念区别

ECMAScript 5(ES5)和 ECMAScript 6(ES6,也称为 ES2015)都引入了对“类”的支持,但它们的实现方式和理念有所不同。以下是 ES5 和 ES6 中类的主要概念和区别:

1. ES5 中的类

在 ES5 中,没有正式的类语法。相反,类的实现是基于构造函数和原型的方法。

定义一个类(构造函数)

在 ES5 中,创建“类”的方式是使用构造函数和原型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义一个 Person 类
function Person(name, age) {
this.name = name;
this.age = age;
}

// 在原型上添加方法
Person.prototype.sayHello = function() {
console.log("Hello, my name is " + this.name);
};

// 创建实例
var john = new Person("John", 30);
john.sayHello(); // 输出: Hello, my name is John

2. ES6 中的类

ES6 引入了更为通用和简洁的 class 语法,使得面向对象编程的代码更加清晰、易读。

定义一个类

在 ES6 中,使用 class 关键字来定义类,使用 constructor 方法来定义构造函数,并且可以直接在类中定义方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义一个 Person 类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}

sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
}

// 创建实例
const john = new Person("John", 30);
john.sayHello(); // 输出: Hello, my name is John

3. 主要区别

3.1. 语法

  • ES5:使用构造函数和原型链。
  • ES6:使用 class 关键字,语法更加清晰。

3.2. 原型继承

  • ES5:继承通常通过设置原型对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function Employee(name, age, position) {
    Person.call(this, name, age);
    this.position = position;
    }

    Employee.prototype = Object.create(Person.prototype);
    Employee.prototype.constructor = Employee;

    Employee.prototype.sayPosition = function() {
    console.log(`I am a ${this.position}`);
    };
  • ES6:使用 extends 关键字实现类的继承,使代码更简洁。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Employee extends Person {
    constructor(name, age, position) {
    super(name, age); // 调用父类构造函数
    this.position = position;
    }

    sayPosition() {
    console.log(`I am a ${this.position}`);
    }
    }

3.3. 静态方法

  • ES5:没有专门的语法定义静态方法,只能通过直接在构造函数上添加方法来实现。

    1
    2
    3
    Person.staticMethod = function() {
    console.log("This is a static method.");
    };
  • ES6:可以使用 static 关键字轻松定义静态方法。

    1
    2
    3
    4
    5
    class Person {
    static staticMethod() {
    console.log("This is a static method.");
    }
    }

3.4. Getter 和 Setter

  • ES5:不支持使用简洁语法定义 getter 和 setter。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function Person(name, age) {
    this.name = name;
    this.age = age;
    }

    Object.defineProperty(Person.prototype, "age", {
    get: function() {
    return this._age;
    },
    set: function(value) {
    this._age = value > 0 ? value : 0;
    }
    });
  • ES6:可以使用 getset 关键字定义。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Person {
    constructor(name, age) {
    this.name = name;
    this._age = age;
    }

    get age() {
    return this._age;
    }

    set age(value) {
    this._age = value > 0 ? value : 0;
    }
    }

4. 总结

  • ES5 中的类是通过构造函数和原型实现的,而 ES6 引入了更直观的 class 语法,增强了面向对象编程的表达力。
  • ES6 大大简化了类和继承的定义,使得代码更加清晰、易于理解。
  • ES6 还提供了 Getter 和 Setter,以及静态方法等新特性,使得定义类的能力更为丰富。

浏览器的盒模型

标准盒模型

设置box-sizing:content-box默认值

标准盒模型遵循 W3C 的 CSS 规范,是现代浏览器(如 Chrome、Firefox、Safari 和 IE 11 及更新版本)默认的盒模型处理方式。该模型的计算方式如下:

  • 元素的实际宽度和高度内容区域的宽度和高度(widthheight 属性)组成,不包括内边距(padding)、边框(border)和外边距(margin)。
  • 公式
    • 总宽度 = width + padding-left + padding-right + border-left + border-right + margin-left + margin-right
    • 总高度 = height + padding-top + padding-bottom + border-top + border-bottom + margin-top + margin-bottom

怪异盒模型

设置box-sizing:border-box就可以使用怪异盒模型了

怪异盒模型主要用于老版本的浏览器(特别是 IE5 和 IE6),在这些浏览器中,widthheight 属性的计算方式与标准盒模型不同。具体来说,widthheight 属性的值包括内边距和边框。

  • 公式:
    • 总宽度 = width + padding + border + margin
    • 总高度 = height + padding + border + margin

北京商越网络科技有限公司—视频面试(技术面—实习面试)全局38分钟

Vue部分

couputed和watch的区别

Computed 计算属性

  • 缓存:计算属性是基于它们的依赖进行缓存的。只有当计算属性所依赖的数据发生变化时,它才会重新计算。这意味着如果依赖没有变化,多次访问计算属性会立即返回之前计算的结果,而不需要再次执行函数。
  • 依赖性:计算属性是声明式的,它的值是依赖于其他数据自动计算出来的。当依赖的数据发生改变时,计算属性的值也会随之更新。
  • 使用场景:当你需要根据其他数据动态地计算出一个值,并且这个值会被多次用到时,使用计算属性会更高效。例如,过滤列表或计算总和等。

Watch 监听器

  • 无缓存:监听器不会缓存结果,每次触发都会执行指定的回调函数。
  • 灵活性:监听器可以执行异步操作或者开销较大的操作,比如发起网络请求、修改多个状态等。
  • 使用场景:当你需要在数据变化时执行一些复杂的逻辑,或者需要执行异步操作时,应该使用监听器。例如,当某个值变化时向服务器发送数据更新。

总结

  • Computed 更适合用于简单的数据处理和衍生计算,这些计算是基于现有的数据并且希望利用Vue的响应式系统来自动管理这些计算。
  • Watch 则更适合处理数据变化后需要执行的一些复杂逻辑,尤其是那些涉及到异步操作或多个状态更改的情况。

Vue2和Vue3中响应式定义有什么区别?

主要区别

  1. API 设计
    • Vue 2:使用 data 选项,必须是一个函数,返回一个对象。
    • Vue 3:使用 setup 函数和 Composition API,提供了 refreactive 两种方式来定义响应式数据。
  2. 响应式机制
    • Vue 2:基于 Object.defineProperty,只能劫持已存在的属性,无法监听新属性的添加。
    • Vue 3:基于 Proxy,可以监听对象属性的增删和数组的变化。
  3. 灵活性和复用性
    • Vue 2:选项式 API(Options API)将逻辑分散在不同的选项中(如 datamethodscomputed 等),适合简单的组件。
    • Vue 3:Composition API 提供了更灵活的逻辑组织方式,可以将相关的逻辑封装在一起,便于复用和测试。
  4. TypeScript 支持
    • Vue 2:对 TypeScript 的支持相对较弱,需要额外的配置和类型声明。
    • Vue 3:对 TypeScript 的支持更好,提供了更好的类型推断和类型安全。

总结

Vue 3 通过引入 Composition API 和 refreactive 等新特性,提供了更灵活、更强大的响应式数据定义方式。这些改进不仅提高了代码的可维护性和复用性,还增强了对 TypeScript 的支持,使得 Vue 3 成为了一个更加现代化和高效的前端框架。

三大件部分

CSS选择器有哪些?优先级排序?

CSS选择器是用于选取文档中的元素并应用样式规则的关键工具。下面是一些常见的CSS选择器类型及其优先级排序(从高到低):

CSS选择器类型

  1. 内联样式 (style 属性) - 直接在HTML标签中定义的样式,如 <div style="color: red;">Hello</div>
  2. ID选择器 (#id) - 通过元素的ID来选择元素,如 #header { color: blue; }
  3. 类选择器 (.class) - 通过元素的类名来选择元素,如 .error { color: red; }
  4. 属性选择器 ([attr]) - 通过元素的属性来选择元素,如 [type="text"] { border: 1px solid black; }
  5. 伪类选择器 (:pseudo-class) - 用于选择处于特定状态下的元素,如 a:hover { color: green; }
  6. 伪元素选择器 (::pseudo-element) - 用于选择元素的特定部分,如 p::first-line { font-weight: bold; }
  7. 标签选择器 (element) - 通过元素名称来选择元素,如 div { margin: 10px; }
  8. 通用选择器 (*) - 选择所有元素,如 * { box-sizing: border-box; }
  9. 子选择器 (>) - 选择作为某个元素直接子元素的所有元素,如 ul > li { list-style: none; }
  10. 相邻兄弟选择器 (+) - 选择紧接在另一个元素后的元素,前提是两个元素拥有相同的父元素,如 h1 + p { margin-top: 0; }
  11. 一般兄弟选择器 (~) - 选择前一个元素之后的所有兄弟元素,如 h1 ~ p { color: gray; }
  12. 组合选择器 - 将多个选择器组合在一起,如 .container .item, .container .highlight { background-color: yellow; }

优先级排序

CSS选择器的优先级计算遵循以下规则:

  • 内联样式:1000
  • ID选择器:100
  • 类选择器、属性选择器、伪类选择器:10
  • 标签选择器、伪元素选择器:1
  • 通用选择器、组合选择器:0

优先级是累加的,即一个选择器的最终优先级是其内部各个部分优先级的总和。例如,选择器 .class #id element 的优先级为 111(10 + 100 + 1)。

当多个规则应用于同一个元素时,具有更高优先级的选择器将覆盖优先级较低的选择器。如果优先级相同,则最后定义的规则生效。

相对定位和绝对定位他们都是相对于谁进行定位的?

在CSS中,相对定位(position: relative;)和绝对定位(position: absolute;)都是用于控制元素在页面上的位置,但它们的定位基准点不同。下面是它们各自的定位基准点:

相对定位(position: relative;

  • 定位基准点:相对定位的元素是相对于其正常位置进行定位的。也就是说,元素的位置会基于它在标准文档流中的位置进行偏移。
  • 影响布局:相对定位的元素仍然占据原来的空间,不会影响其他元素的布局。
  • 示例
    1
    2
    3
    4
    5
    .relative {
    position: relative;
    top: 10px;
    left: 20px;
    }
    在这个例子中,.relative 元素会从它原本的位置向下移动10像素,向右移动20像素,但原来的布局空间仍然保留。

绝对定位(position: absolute;

  • 定位基准点:绝对定位的元素是相对于最近的非 static 定位的祖先元素进行定位的。如果没有任何这样的祖先元素,则相对于初始包含块(通常是浏览器窗口)进行定位。
  • 影响布局:绝对定位的元素会脱离标准文档流,不再占据原来的空间,因此不会影响其他元素的布局。
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    .parent {
    position: relative; /* 使 .child 相对于 .parent 定位 */
    }

    .child {
    position: absolute;
    top: 10px;
    left: 20px;
    }
    在这个例子中,.child 元素会相对于 .parent 元素进行定位,从 .parent 的左上角向下移动10像素,向右移动20像素。

关键点总结

  • 相对定位:相对于自身正常位置进行定位,仍然占据原来的空间。
  • 绝对定位:相对于最近的非 static 定位的祖先元素进行定位,脱离标准文档流,不占据原来的空间。

理解这些定位方式的基准点和行为,可以帮助你更有效地控制页面布局和元素位置。

CSS提高性能的方式有哪些?

项目部分

项目一:易优评—评分系统

项目中会有权限校验这部分吗?你是如何做的权限校验呢?

回答:有的,在ruoyi-vue前后端分离版的框架中,权限校验在前端是通过一个权限校验函数进行的。

(这样回答大概率不行) 由于校验函数是框架自带的,它被封装成了一个自定义指令,v-Permission,通过这个指令然后再传入数组类型的,以:分割的权限字符串即可完成。

项目中你主要做了什么?

回答一:评分页面的编写,查询总项目页面的编写,创建一般赛事页面的编写等等各种交互页面。

你在项目中有没有遇到过什么问题?

  1. 在线预览ppt
  2. 参数携带问题

如何解决的问题?

1.对于在线预览ppt我们解决的方法:

通过查询百度,搜寻了以下解决方式:1.通过vue-office这个库,来实现docx、pdf、excel等文件的在线预览,具体使用方法:https://toscode.mulanos.cn/danill/vue-office

项目二:AI图生图

你的项目中写到了你使用了上传,请问你是如何实现上传功能的呢?

  1. 通过ElementUIPlus中的el-upload组件,可以实现上传功能。
  2. 通过input标签指定type为file来实现上传,如需接受多个上传文件,可指定属性multiple来上传多个文件,只是这种上传方式要求用户一次选择多个文件,而不能一次选择一个。

你的项目有提到将返回的图片展示在页面上,这个过程是怎么实现的呢?

当我们将图片上传,并按照自己的要求准备点击生成图片时,此时会进行发送Axios发送网络请求来请求接口,我们会获得返回包含有处理过后图片的base64格式的文件,我们只需要将该base64指定为img标签的src属性即可让图片成功显示在前端页面。

上海飞未(技术面—实习面试(通过))全局35分钟

开篇

1.以下代码的输出结果是?

1
2
3
4
5
for(let i = 1; i<= 5; i++){
setTimeout(function () {
console.log(i);
}, i * 1000)
}

解析

  1. 循环
    • for (let i = 1; i <= 5; i++):循环从1到5执行。
    • 使用 let 关键字声明变量 i,这意味着每次循环迭代都会创建一个新的 i 变量,每个 i 都有自己的作用域。
  2. 异步执行
    • setTimeout 是一个异步操作,它会将回调函数放入事件队列中,在指定的时间延迟后执行。
    • 在每次循环迭代中,setTimeout 被调用,设置一个延迟为 i * 1000 毫秒的回调函数,该回调函数将在未来某个时间点执行并输出 i 的值。
  3. 闭包
    • 由于每次迭代都创建了一个新的 i 变量,每个 setTimeout 回调函数都会引用它自己的 i 变量,而不是共享同一个 i

2.以下代码的输出结果?

1
2
3
4
5
for (var i = 5; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000)
}

解析

在这段代码中,setTimeout是一个异步操作,它会将回调函数放入事件队列中,在指定的时间延迟后执行。当所有setTimeout都被设置时,循环已经完成,此时变量i的值已经是6(因为循环最后一次迭代后i++使i变为6,然后循环停止)。因此,当每个setTimeout的回调函数最终被执行时,它们都引用了同一个变量i,其值已经是6。

3.以下代码的输出结果是?

1
2
3
4
5
6
7
(function(){
for (let j = 1; j <= 5; j++) {
setTimeout(function () {
console.log(j);
}, j * 1000)
}
})()

解析

  1. IIFE 调用:
    • (function(){ ... })() 这个IIFE被立即执行。
  2. 循环:
    • for (let j = 1; j <= 5; j++) 循环从1到5执行。
    • 使用 let 关键字声明变量 j,这意味着每次循环迭代都会创建一个新的 j 变量,每个 j 都有自己的作用域。
  3. 闭包和异步执行:
    • setTimeout 是异步的,这意味着所有的 setTimeout 调用都会在循环完成后才开始计时。
    • 由于每次迭代都创建了一个新的 j 变量,每个 setTimeout 回调函数都会引用它自己的 j 变量,而不是共享同一个 j

中途

当然可以,下面是对你提出的问题的详细回答:

1. HTML常用标签

HTML(HyperText Markup Language)是用于创建网页的标准标记语言。以下是一些常用的HTML标签:

  • 文本格式化标签

    • <b><strong>:加粗文本
    • <i><em>:斜体文本
    • <u>:下划线
    • <small>:小字体
    • <mark>:高亮显示
  • 结构标签

    • <div>:块级容器
    • <span>:内联容器
    • <header>:页眉
    • <footer>:页脚
    • <nav>:导航链接
    • <article>:独立的内容块
    • <section>:文档或应用的一部分
    • <aside>:侧栏内容
  • 表单标签

    • <form>:定义表单
    • <input>:输入字段
    • <textarea>:多行文本输入
    • <button>:按钮
    • <label>:标签
    • <select><option>:下拉列表
  • 列表标签

    • <ul>:无序列表
    • <ol>:有序列表
    • <li>:列表项
    • <dl><dt><dd>:定义列表
  • 图像和媒体标签

    • <img>:图像
    • <audio>:音频
    • <video>:视频
  • 链接标签

    • <a>:超链接

2. CSS常用属性

CSS(Cascading Style Sheets)用于描述HTML文档的样式。以下是一些常用的CSS属性:

  • 布局属性

    • display:定义元素的显示类型(如 block, inline, flex, grid
    • position:定义元素的定位方式(如 static, relative, absolute, fixed, sticky
    • float:定义元素的浮动行为
    • clear:清除浮动
    • flexgrid:弹性布局和网格布局属性
  • 尺寸属性

    • widthheight:定义元素的宽度和高度
    • max-widthmin-width:最大和最小宽度
    • max-heightmin-height:最大和最小高度
    • padding:内边距
    • margin:外边距
    • border:边框
  • 文本属性

    • color:文本颜色
    • font-family:字体系列
    • font-size:字体大小
    • font-weight:字体粗细
    • text-align:文本对齐方式
    • text-decoration:文本装饰(如下划线、删除线)
  • 背景属性

    • background-color:背景颜色
    • background-image:背景图像
    • background-repeat:背景图像重复方式
    • background-position:背景图像位置

3. Vue的watch和computed属性的区别

  • watch

    • 用途:用于观察和响应Vue实例上的数据变动。
    • 工作原理:当被监听的数据发生变化时,回调函数会被触发。
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <script>
      export default {
      data() {
      return {
      message: 'Hello'
      };
      },
      watch: {
      message(newVal, oldVal) {
      console.log(`message changed from ${oldVal} to ${newVal}`);
      }
      }
      };
      </script>
  • computed

    • 用途:用于定义计算属性,依赖于其他数据属性,当依赖的数据发生变化时,计算属性会自动重新计算。
    • 工作原理:计算属性是基于它们的依赖缓存的,只有当依赖的数据发生变化时,计算属性才会重新求值。
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      <script>
      export default {
      data() {
      return {
      firstName: 'John',
      lastName: 'Doe'
      };
      },
      computed: {
      fullName() {
      return `${this.firstName} ${this.lastName}`;
      }
      }
      };
      </script>

4. Vue常用的API(内置指令、watch、computed)

  • 内置指令

    • v-bind:动态绑定属性
    • v-on:绑定事件监听器
    • v-ifv-show:条件渲染
    • v-for:列表渲染
    • v-model:双向数据绑定
    • v-slot:插槽分发
  • watch

    • 观察数据变化,触发回调函数。
  • computed

    • 定义计算属性,自动重新计算。

5. Vuex和Pinia

  • Vuex

    • 简介:Vue.js 的状态管理库,用于集中管理应用的状态。
    • 特点
      • 中央存储:所有组件的状态都存储在一个全局对象中。
      • 单一状态树:整个应用的状态被存储在一个单一的地方。
      • 模块化:可以将状态分成多个模块,便于管理和维护。
    • 主要概念
      • state:存储状态。
      • getters:类似于计算属性,用于派生状态。
      • mutations:同步修改状态的方法。
      • actions:异步操作,可以包含异步逻辑。
  • Pinia

    • 简介:新一代的Vue状态管理库,旨在替代Vuex。
    • 特点
      • 更简洁的API:更少的样板代码。
      • 类型推断:更好的TypeScript支持。
      • 模块化:每个store都是一个单独的模块,易于组合和复用。
    • 主要概念
      • defineStore:定义一个store。
      • setup 方法:用于定义store的状态、动作和获取器。
      • useStore:在组件中使用store。

希望这些信息对你有所帮助!如果有更多问题或需要进一步解释,请随时提问。

最后

如果你在工作中遇到了困难,你会怎么做(假设你在实习时是有师傅带的?)

在工作中遇到困难时,特别是有师傅带的情况下,可以采取以下几个步骤来解决问题:

1. 自我排查

首先,尝试自己解决问题。这有助于锻炼你的独立思考能力和问题解决能力。你可以:

  • 查阅文档:查看官方文档、API 文档等,寻找相关的信息。
  • 搜索引擎:利用搜索引擎查找类似问题的解决方案。
  • 调试代码:逐步调试代码,找出问题所在。
  • 回顾学习资料:回顾之前的学习资料或课程内容,看看是否有相关内容可以帮助你。

2. 整理问题

如果自己无法解决问题,需要向师傅求助时,先整理清楚问题的具体情况。这包括:

  • 问题描述:清晰、具体地描述问题是什么。
  • 已尝试的方法:说明你已经尝试过哪些方法,为什么这些方法没有解决问题。
  • 期望的结果:说明你希望达到什么样的效果。
  • 当前的结果:展示你当前的代码和运行结果。

3. 请教师傅

找到合适的时机,向师傅请教问题。可以采用以下方式:

  • 面对面交流:直接找师傅当面讨论,这样可以更直观地展示问题。
  • 书面沟通:如果师傅比较忙,可以通过邮件或即时消息工具发送问题,附上详细的描述和代码片段。
  • 团队会议:如果合适,可以在团队会议中提出问题,大家一起讨论解决方案。

4. 记录反馈

师傅给出建议或解决方案后,及时记录下来,并进行实践。如果还有疑问,可以再次请教。

  • 笔记:记录师傅的建议和解决方案,方便以后参考。
  • 实践:按照师傅的指导进行实践,验证是否解决问题。
  • 总结:问题解决后,总结经验教训,反思自己在处理问题过程中的不足之处。

5. 主动学习

即使问题解决了,也要保持学习的态度,不断提升自己的技能。

  • 扩展知识:了解与问题相关的更多知识和最佳实践。
  • 分享经验:如果可能,可以将解决问题的经验分享给团队成员,共同进步。
  • 持续改进:总结这次经历,思考如何在未来遇到类似问题时更快地解决。

上海飞未(人事面—实习复试—过了但是拒绝offer)全局35分钟

全程面试未提到技术点,全部都是关于自身,以及围绕了项目获得了那些收获,家里情况,住在哪里,问了些软著,锻炼了自身什么能力等等,工作中的偏虚的问题。

北京纷享销客面试(未过:算法基础不行)

大部分时间是在做题

1.去重排序, 去除 undefined null false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var a = [1, 19, '19', 8, 23, 0, undefined, null, false];

// 使用 filter 方法过滤掉 undefined, null, false
var filtered = a.filter(item => item !== undefined && item !== null && item !== false);

// 使用 Set 去重,然后转换回数组
var unique = Array.from(new Set(filtered));

// 对数组进行排序
unique.sort((a, b) => {
// 如果两个值都是数字,则按数字大小排序
if (typeof a === 'number' && typeof b === 'number') {
return a - b;
}
// 如果两个值中有一个是字符串,则先将数字转换为字符串再比较
return String(a).localeCompare(String(b));
});

console.log(unique); // 输出去重并排序后的数组

2.返回相加为9的两个数的索引(哈希表法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var nums = [2, 7, 11, 15];
var target = 9;

function twoSum(nums, target) {
var map = {}; // 创建一个哈希表来存储数值和对应的索引

for (var i = 0; i < nums.length; i++) {
var complement = target - nums[i]; // 计算目标值与当前值的差值

// 检查哈希表中是否已经存在这个差值
if (map.hasOwnProperty(complement)) {
// 如果存在,返回当前索引和差值的索引
return [map[complement], i];
}

// 将当前数和它的索引存入哈希表
map[nums[i]] = i;
}

// 如果没有找到解,返回空数组或其他适当的值
return [];
}

// 测试函数
var result = twoSum(nums, target);
console.log(result); // 应该输出 [0, 1]

解释:

  1. 初始化哈希表:创建一个空的对象 map 用来存储数组中的数及其索引。
  2. 遍历数组:使用一个 for 循环遍历数组 nums
  3. 计算补数:对于每个数,计算它与目标值 target 的差值 complement
  4. 检查补数是否已存在:如果 complement 已经存在于 map 中,说明找到了一对数,它们的和等于目标值 target。此时,返回这两个数的索引。
  5. 存储当前数:如果 complement 不在 map 中,将当前数及其索引存入 map
  6. 返回结果:如果没有找到符合条件的数对,返回一个空数组。

在这个例子中,nums = [2, 7, 11, 15]target = 9,函数将返回 [0, 1],因为 nums[0] + nums[1] = 2 + 7 = 9

3.下面这个函数,输出什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function test(){
return new Promise((resolve,reject) => {
console.log(2);
setTimeout(() => {
console.log(1)
reject(112)
},200) //放入队列中
})
.then((res) => {
console.log(3);
})
.catch( async (res) => {
console.log(4);
return await new Promise((resolve,reject) => {
setTimeout(() => {
console.log(5);
reject('end');
}, 200);
})
})
.catch((res) => {
console.log(res);
}).then((res) => {
console.log(6)
})}

解析:

在您的代码中,您创建了一个异步函数 test,该函数返回一个 Promise,并且在这个 Promise 链中包含了多个 .then.catch 处理器。下面是对这段代码执行流程的解析:

  1. 当调用 test 函数时,首先会立即执行 Promise 构造函数中的同步部分,输出 2
  2. 然后,setTimeout 被调用,其回调函数会被安排在当前任务的微任务之后执行,即大约200毫秒后输出 1 并拒绝 Promise,传递值 112catch 方法。
  3. 因为 Promise 在构造函数内部被拒绝(通过 reject(112)),所以控制权跳过第一个 .then,直接进入第一个 .catch
  4. 在第一个 .catch 中,输出 4,然后返回一个新的 Promise,这个 Promise 会在大约200毫秒后拒绝,并传递值 'end'
  5. 第一个 .catch 返回的 Promise 被拒绝后,控制权转到下一个 .catch,输出 'end'
  6. 最后,因为最后一个 .catch 没有抛出新的错误或返回被拒绝的 Promise,所以接下来的 .then 会被调用,输出 6

总结一下,当调用 test 函数时,控制台的输出顺序将是:

  • 2 (立即)
  • 4Promise 被拒绝后立即)
  • 1 (约200毫秒后)
  • 5 (约400毫秒后,即第一个 setTimeout 后的200毫秒)
  • end'end' 是传递给第二个 .catch 的值)
  • 6 (在所有 catch 之后)

请注意,15 的实际输出时间取决于计算机的性能和当前的任务队列状态,但它们之间的相对顺序是确定的,即 1 总是在 5 之前输出。

4.给你一段代码,分析输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var num = 100;
var obj = {
num: 200,
inner:{
num:300,
print: function(){
console.log(this.num);
}
}
};
// 1)obj.inner.print();
// 2)var func = obj.inner.print;
// func();
// 3)(obj.inner.print)();
// 4)(obj.inner.print = obj.inner.print)();
// 5) 利用obj.inner.print函数输出200

让我们逐一分析这些情况,并解释为什么它们会输出不同的结果。

1. obj.inner.print();

1
obj.inner.print();
  • 解释:在这个调用中,this 指向 obj.inner,因为 print 方法是作为 obj.inner 的属性被调用的。
  • 输出:300

2. var func = obj.inner.print; func();

1
2
var func = obj.inner.print;
func();
  • 解释:在这里,funcobj.inner.print 的引用,但它被作为一个普通的函数调用,而不是作为对象的方法调用。在这种情况下,this 指向全局对象(在浏览器中是 window,在 Node.js 中是 global)。由于全局对象有 num 属性,var定义的,所以this.num 会是 100
  • 输出:100

3. (obj.inner.print)();

1
(obj.inner.print)();
  • 解释:这与 obj.inner.print() 类似,this 指向 obj.inner,因为 print 方法是作为 obj.inner 的属性被调用的。
  • 输出:300

4. (obj.inner.print = obj.inner.print)();

1
(obj.inner.print = obj.inner.print)();
  • 解释:这里的 obj.inner.print = obj.inner.print 实际上是一个赋值表达式,它会返回 obj.inner.print 的引用。但是,由于赋值表达式的结果是一个独立的函数引用,而不是作为对象的方法调用,this 指向全局对象(在浏览器中是 window,在 Node.js 中是 global)。因此,this.num 会是 100
  • 输出:100

5. 利用 obj.inner.print 函数输出 200

要利用 obj.inner.print 函数输出 200,我们需要确保 this 指向 obj,而不是 obj.inner。可以通过以下几种方式实现:

方法 1:使用 callapply

1
obj.inner.print.call(obj);
  • 解释call 方法允许你显式地指定 this 的值。这里我们将 this 设置为 obj
  • 输出:200

方法 2:使用 bind

1
2
var boundFunc = obj.inner.print.bind(obj);
boundFunc();
  • 解释bind 方法返回一个新函数,该函数的 this 被永久绑定到指定的对象。这里我们将 this 绑定到 obj
  • 输出:200

总结

  • 1) obj.inner.print(); 输出 300
  • 2) var func = obj.inner.print; func(); 输出 undefined
  • 3) (obj.inner.print)(); 输出 300
  • 4) (obj.inner.print = obj.inner.print)(); 输出 undefined
  • 5) 利用 obj.inner.print 函数输出 200 的方法:
    • obj.inner.print.call(obj);
    • var boundFunc = obj.inner.print.bind(obj); boundFunc();

希望这些解释能帮助你更好地理解 JavaScript 中的 this 绑定机制。

北京比邻星空前端实习一面(未过)

1.开篇自我介绍

2.问答环节

1. JS的数据类型有哪些?

JavaScript有七种原始数据类型和一种复合数据类型:

  • 布尔型(Boolean):表示逻辑值,有两个字面量:truefalse
  • 数字型(Number):用于表示整数或浮点数。在ES6中也包含了特殊的数值如 Infinity, -Infinity, 和 NaN
  • 字符串型(String):用于表示文本数据,由单引号、双引号或反引号包围的字符序列。
  • 符号型(Symbol):是ES6新增的一种基本数据类型,用来定义对象的唯一属性键。
  • 未定义型(Undefined):变量被声明了但没有赋值时的默认值。
  • 空值型(Null):表示一个空的对象引用。
  • Bigint:ES2020引入,可以表示比 Number 类型更大的整数。

以及一种复合数据类型:

  • 对象(Object):是一种非原始类型的复杂数据结构,可以包含多个属性和方法。

2. 怎么实现一个元素垂直水平居中

使用CSS可以通过多种方式来实现元素的垂直和水平居中:

  • 使用Flexbox布局:将父容器设置为 display: flex; justify-content: center; align-items: center;
  • 使用绝对定位与转换:对于已知尺寸的子元素,可以在父级设置 position: relative;,然后对子元素设置 position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  • 使用Grid布局:通过设置父容器为 display: grid; place-items: center; 来使子元素居中。

3. flex布局中,flex属性设置代表什么?flex:1什么意思?

在CSS的Flexbox布局中,flex属性是一个简写属性,它结合了三个不同的属性:flex-grow, flex-shrink, 和 flex-basis。这个属性定义了一个弹性项目的灵活性,也就是当有剩余空间或需要缩小以适应容器时,项目如何增长、收缩以及初始大小。

flex 属性的语法如下:

1
flex: <flex-grow> <flex-shrink> <flex-basis>;
  • flex-grow: 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
  • flex-shrink: 定义项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
  • flex-basis: 定义分配之前项目的初始主轴尺寸,可以是长度(如 20%, 75px)或者关键词 auto(默认值),后者根据内容来确定宽度或高度。

你可以只指定一个或两个值,而省略其他值,此时浏览器会使用默认值填充未指定的部分。例如:

1
2
3
4
flex: 2; /* 等同于 flex: 2 1 auto; */
flex: 2 1; /* 等同于 flex: 2 1 auto; */
flex: auto; /* 等同于 flex: 1 1 auto; */
flex: none; /* 等同于 flex: 0 0 auto; */

flex: none 是一种特殊情况,它会让项目不放大也不缩小,并且它的基础尺寸就是其内容的实际尺寸。

此外,你也可以用具体的长度或百分比来设置 flex-basis 的值,这会影响元素在主轴上的起始尺寸:

1
flex: 1 1 200px; /* 元素的基础尺寸是200px,但可以按比例增长和缩小 */

flex 属性非常适合用于创建响应式设计,允许开发者轻松地控制项目在不同屏幕尺寸下的行为。

当在CSS中设置 flex: 1 时,这实际上是一个简写,它等同于设置了以下三个属性:

1
2
3
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;

上述属性对创建响应式布局非常有帮助,当你给一个或多个弹性项目设置 flex: 1,你是在告诉浏览器,这些项目应该均匀地分享容器内所有可用的空间,并且在需要的时候也可以相应地缩小。

4. 如何利用CSS控制一个元素的无限旋转?

要让一个元素无限旋转,你可以使用CSS动画配合 @keyframes 规则:

1
2
3
4
5
6
7
8
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

.element-to-spin {
animation: spin infinite linear 1s;
}

这段代码会让 .element-to-spin 类的元素以线性时间函数每秒旋转一周,并且循环无数次(infinite)。

5. null和undefined有什么区别?

  • null 是一个表示“没有值”的对象,通常用于有意地表示某个变量还没有指向任何对象或者有意地释放对象引用。
  • undefined 表示一个变量已经被声明但是尚未初始化,或者是对象中不存在的属性的值,或者是函数没有返回值的情况。

6. v-cloak接触过吗?有什么用法?

v-cloak 是Vue.js中的一个指令,它会保持在元素上直到关联实例完成编译。结合CSS规则,它可以隐藏未编译的Mustache标签直到视图完全渲染。例如:

1
[v-cloak] { display: none; }

这行样式会确保带有 v-cloak 的元素在Vue实例编译之前不会显示出来。

7. 如何判断一个元素是不是数组?有那些方法?

判断一个值是否为数组,JavaScript 提供了多种方法。以下是几种常用的方法:

  1. Array.isArray()

    • 这是检查一个对象是否为数组的最直接和推荐的方法。
      1
      2
      Array.isArray([]); // true
      Array.isArray({}); // false
  2. instanceof 操作符

    • 如果你知道数组是在当前文档中创建的,你可以使用 instanceof 来检查。
      1
      [] instanceof Array; // true
    • 但是请注意,instanceof 在跨iframe或window环境时可能会失效,因为每个窗口都有自己的 Array 构造函数。
  3. Object.prototype.toString.call()

    • 这种
      1
      Object.prototype.toString.call([]) === '[object Array]'; // true
  4. constructor 属性

    • 检查对象的构造函数是否为 Array
      1
      [].constructor === Array; // true
    • instanceof 类似,这种方法也可能在跨窗体环境中出现问题。
  5. 鸭式辨型 (Duck Typing)

    • 虽然这不是一种标准的方法,但在某些情况下可以通过检查特定属性或方法的存在来推断一个对象是不是数组(例如检查是否存在 .length 属性)。
      1
      2
      3
      4
      var maybeArray = [];
      if ('length' in maybeArray && typeof maybeArray.push === 'function') {
      console.log('可能是数组');
      }
    • 注意这种方法并不完全可靠,因为它也可能匹配非数组的对象。
  6. Polyfill for Array.isArray

    • 对于非常老旧的浏览器(如IE8及更早版本),你可能需要提供 Array.isArray 的polyfill:
      1
      2
      3
      4
      5
      if (!Array.isArray) {
      Array.isArray = function(arg) {
      return Object.prototype.toString.call(arg) === '[object Array]';
      };
      }
  7. 第三方库提供的工具函数

    • 某些流行的JavaScript库,比如 Lodash 或 Underscore.js,提供了方便的工具函数来检查数组。
      1
      _.isArray([]); // true, 使用Lodash或Underscore.js

8. v-show和v-if有什么区别?

v-showv-if 都是用来条件渲染元素的Vue指令,但它们的工作方式不同:

  • v-if 是“真正的”条件渲染,因为它会确保在切换过程中有条件的部分甚至不会被渲染到DOM中;也就是说,当条件为假时,元素将完全从DOM中移除。
  • v-show 则是始终渲染节点到DOM中,并通过CSS的 display 属性来控制其可见性。这意味着无论条件真假,元素都会存在于DOM中,只是有时会被隐藏。

因此,v-if 更适合用于在运行时很少改变条件的场景,而 v-show 则适用于频繁切换的场景。

计算机网络基础

HTTP协议

一、HTTP概述

HTTP(HyperText Transfer Protocol)是一个用于从Web服务器传输超文本到本地浏览器的传输协议。它是Web的基础,主要用来在客户端(通常是浏览器)和服务器之间传输数据。

二、HTTP的特点

  1. 无状态性: 每个请求都是独立的,服务器不会记住之前的请求状态。
  2. 简单: HTTP协议相对简单,采用请求-响应模式。
  3. 灵活性: HTTP支持多种数据格式,不仅限于HTML,还可以传输文本、图片、音频、视频等。
  4. 可扩展性: 可以通过头信息扩展协议功能,比如添加缓存控制、内容编码等。

三、HTTP的请求和响应结构

1. HTTP请求

HTTP请求分为几个部分:

  • 请求行: 包含方法、请求URI和HTTP版本。例如:GET /index.html HTTP/1.1

    • 方法: 常见方法有GET、POST、PUT、DELETE等。
    • URI: 统一资源标识符,指向请求的资源。
    • HTTP版本: 一般使用HTTP/1.1或HTTP/2。
  • 请求头: 提供客户端信息和请求上下文。例如:

    1
    2
    3
    Host: www.example.com
    User-Agent: Mozilla/5.0
    Accept: text/html
  • 请求体: (针对POST和PUT等方法) 包含要发送给服务器的数据。

2. HTTP响应

HTTP响应同样分为几个部分:

  • 状态行: 包含HTTP版本、状态码和状态描述。例如:HTTP/1.1 200 OK

    • 状态码: 常见状态码有200(成功)、404(未找到)、500(服务器错误)等。
  • 响应头: 提供服务器信息和响应上下文。例如:

    1
    2
    Content-Type: text/html
    Content-Length: 1234
  • 响应体: 包含实际返回的数据内容(HTML文件、图片等)。

四、HTTP状态码

状态码用于表示请求的处理结果,分为五类:

  • 1xx: 信息性状态码(继续请求中等)。
  • 2xx: 成功状态码(如200表示请求成功)。
  • 3xx: 重定向状态码(如301表示永久重定向)。
  • 4xx: 客户端错误状态码(如404表示请求的资源未找到)。
  • 5xx: 服务器错误状态码(如500表示服务器内部错误)。

五、请求方法详解

  1. GET: 请求指定资源,通常用于获取数据,不应有副作用。
  2. POST: 向指定资源提交数据,可能会改变服务器状态或引起副作用。
  3. PUT: 更新现有资源,若资源不存在则创建新的资源。
  4. DELETE: 删除指定的资源。

六、HTTP/1.1与HTTP/2

1. HTTP/1.1

  • 默认连接保持(Persistent Connection),允许多个请求复用同一个TCP连接。
  • 引入了缓存机制,通过Cache-Control、ETag等头来管理缓存。

2. HTTP/2

  • 使用二进制协议,性能更优,减少延迟。
  • 支持多路复用,允许多个请求并发在同一个连接上。
  • 头部压缩(HPACK),降低了头部信息所占用的空间。

七、HTTP的安全性

  1. HTTPS: 在HTTP协议上加上SSL/TLS,以保证数据传输的安全性和完整性。
  2. 安全头: 如 Strict-Transport-SecurityContent-Security-PolicyX-Content-Type-Options 等确保数据传输中的安全。

八、跨域资源共享(CORS)

  • 浏览器出于安全考虑,限制跨域请求。
  • CORS是一种机制,通过HTTP头字段允许前端代码访问其他域的资源。
  • 如何设置CORS:服务器通过设置Access-Control-Allow-Origin等响应头来实现。

九、面试常见问题

  1. HTTP与HTTPS的区别?

    • HTTP是明文传输,HTTPS使用SSL/TLS加密,确保了数据的安全性。
  2. 什么是RESTful API?

    • 一种设计风格,以资源为中心,通过HTTP协议进行CRUD操作,遵循一定的设计原则如URI唯一性和使用标准HTTP方法。
  3. 如何优化HTTP请求?

    • 合并请求(使用雪碧图,合并文件)、使用缓存、启用Gzip压缩、使用CDN等。
  4. 状态码及其使用情况?

    • 状态码组成与含义,如何在不同情况下使用状态码,以及如何处理错误状态。
  5. 讲一下HTTP/2的优势?

    • 多路复用、头部压缩、流优先级、请求减少连接数等。

网络模型

OSI 模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+-------------------+
| 应用层 | 第7层
+-------------------+
| 表示层 | 第6层
+-------------------+
| 会话层 | 第5层
+-------------------+
| 传输层 | 第4层
+-------------------+
| 网络层 | 第3层
+-------------------+
| 数据链路层 | 第2层
+-------------------+
| 物理层 | 第1层
+-------------------+

TCP/IP 模型

1
2
3
4
5
6
7
8
9
+-------------------+
| 应用层 | 第4层
+-------------------+
| 传输层 | 第3层
+-------------------+
| 网络层 | 第2层
+-------------------+
| 链路层 | 第1层
+-------------------+

对比

  • OSI模型:分为7层,每一层都有明确的职责,更理论化。
  • TCP/IP模型:分为4层,更加简化和实用,实际应用中更为常见。

层次对应关系

  • 应用层
    • OSI:第7层
    • TCP/IP:第4层
  • 传输层
    • OSI:第4层
    • TCP/IP:第3层
  • 网络层
    • OSI:第3层
    • TCP/IP:第2层
  • 数据链路层
    • OSI:第2层
    • TCP/IP:第1层(链路层)
  • 物理层
    • OSI:第1层
    • TCP/IP:链路层的一部分

协议示例

  • 应用层
    • OSI/TCP/IP:HTTP、FTP、SMTP、Telnet
  • 传输层
    • OSI/TCP/IP:TCP、UDP
  • 网络层
    • OSI/TCP/IP:IP、ICMP、ARP
  • 数据链路层
    • OSI:Ethernet、PPP、HDLC
    • TCP/IP:以太网、PPP

UDP/TCP协议

1. 概述

传输控制协议 (TCP, Transmission Control Protocol)用户数据报协议 (UDP, User Datagram Protocol) 是传输层的两个主要协议,它们在不同的应用场景中发挥着重要作用。

2. TCP协议

2.1 基本概念

  • 连接导向:TCP是一种面向连接的协议,即在数据传输之前必须先建立连接。
  • 可靠传输:TCP提供可靠的、有序的、错误检查的数据传输服务。
  • 流量控制:通过滑动窗口机制来控制数据的发送速率,防止发送方发送过快导致接收方无法处理。
  • 拥塞控制:通过多种算法(如慢启动、拥塞避免、快速重传、快速恢复)来避免网络拥塞。

2.2 连接建立与关闭(面试重点)

  • 三次握手

    1. 客户端发送SYN(同步序列编号)包到服务器,请求建立连接。
    2. 服务器回应一个SYN-ACK(同步确认)包。
    3. 客户端再发送一个ACK(确认)包,连接建立完成。
  • 图示

    image-20241021210237019

  • 四次挥手

    1. 客户端发送FIN(结束)包,表示不再发送数据。
    2. 服务器回应一个ACK包,确认收到FIN包。
    3. 服务器发送一个FIN包,表示自己也不再发送数据。
    4. 客户端回应一个ACK包,确认收到服务器的FIN包,连接关闭。
  • 图示:

    image-20241021210248623

2.3 报文段结构

  • 源端口:16位,标识发送端的端口号。
  • 目的端口:16位,标识接收端的端口号。
  • 序号:32位,标识本报文段中的第一个字节在数据流中的位置。
  • 确认号:32位,期望收到的下一个字节的序号。
  • 数据偏移:4位,表示报文段头部的长度。
  • 保留:6位,保留字段,目前未使用。
  • 标志位
    • URG:紧急指针有效。
    • ACK:确认号有效。
    • PSH:提示接收方应尽快将数据交给应用层。
    • RST:重置连接。
    • SYN:同步序号,用于建立连接。
    • FIN:结束连接。
  • 窗口大小:16位,表示接收方的接收窗口大小。
  • 校验和:16位,用于检验报文段的完整性。
  • 紧急指针:16位,指出本报文段中的紧急数据的字节数。
  • 选项:可变长,用于提供额外的控制信息。
  • 填充:用于确保头部长度为4字节的整数倍。

2.4 适用场景

  • 适合需要高可靠性的应用,如文件传输、电子邮件、Web浏览等。

2.5 TCP通信示例

TCP是面向字节流数据

image-20241021203931909

3. UDP协议

3.1 基本概念

  • 无连接:UDP是一种无连接的协议,即数据传输前不需要建立连接。
  • 不可靠传输:UDP不保证数据的顺序和可靠性,数据包可能会丢失或乱序到达。
  • 低开销:由于没有复杂的连接管理和错误恢复机制,UDP的开销较低,传输速度快。

3.2 报文结构

  • 源端口:16位,标识发送端的端口号。
  • 目的端口:16位,标识接收端的端口号。
  • 长度:16位,表示整个报文的长度(包括头部和数据)。
  • 校验和:16位,用于检验报文的完整性。校验和是可选的,如果不需要校验,该字段可以设置为0。

3.3 适用场景

  • 适合实时应用,如语音和视频通话、在线游戏等,这些应用对延迟非常敏感,可以容忍一定程度的数据丢失。
  • 适合广播和多播,因为UDP支持广播和多播,而TCP不支持。

3.4UDP通信图示

UDP是面向应用层报文

image-20241021203755250

4. 比较

特性 TCP UDP
连接类型 面向连接 无连接
可靠性 高(有确认机制) 低(无确认机制)
顺序性 保证数据按顺序到达 不保证数据按顺序到达
流量控制 有(滑动窗口机制)
拥塞控制
开销 较高(建立连接、确认机制) 较低(无连接、无确认机制)
适用场景 文件传输、电子邮件、Web浏览 实时应用、广播和多播

5. 实践建议

  • 抓包分析:使用Wireshark等网络抓包工具,捕获和分析TCP和UDP的数据包,了解它们的实际工作过程。
  • 编程实践:编写简单的TCP和UDP客户端和服务器程序,体验两者的不同之处。
  • 阅读文档:阅读RFC文档,深入了解TCP和UDP的规范和细节。

6. 总结

TCP和UDP各有优缺点,适用于不同的应用场景。TCP提供高可靠性和有序的数据传输,适用于需要高可靠性的应用;UDP提供低开销和快速传输,适用于对延迟敏感的应用。理解这两者的特点和工作原理,对于设计和调试网络应用非常重要。