前端面试题汇总

前端

CSDN

  • http://t.csdnimg.cn/M18j6

git pull和git fetch的区别

  • git fetch只是将远程仓库的最新的版本下载到本地,但是不会自动merge,相当于工作区中的文件并没有更新

  • git rebase --continue

版本回退

git reset

  • 彻底回退到指定的commit 版本,该版本后的所有commit将被清除,包括提交历史纪录;
  • 执行后不会产生记录
  • 执行后无法恢复
  • 执行后HEAD会后移

git revert

  • 撤销了指定commit的修改,并不影响后续的commit,但所撤销的commit被后续的commit修改了同一地方则会产生冲突
  • 执行后会产生记录
  • 执行后,不会清除记录,并且会产生新纪录,所以文件不会丢失 HEAD会一直向前

掘金:https://juejin.cn/post/6844903636271644680

高频面试题

原生

  • 盒模型

    • content-box 的 div 的总宽度将是 200px (内容) + 20px (左内边距) + 20px (右内边距) + 10px (左边框) + 10px (右边框) = 260px。总高度同理。

    • border-box 的 div 的总宽度依然是 200px,因为内边距和边框包含在设置的宽度和高度内,总高度同理。

  • BFC(块格式上下文)

    • margin折叠问题
<!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>BFC Example</title>
     <style>
         .container {
             background-color: lightgrey;
         }
 
         .box {
             width: 100px;
             height: 100px;
             background-color: lightblue;
             margin-bottom: 20px;
         }
 
         .container.bfc {
             overflow: hidden; /* Triggers BFC */
         }
     </style>
 </head>
 <body>
     <h2>Without BFC</h2>
     <div class="container">
         <div class="box"></div>
         <div class="box"></div>
     </div>
 
     <h2>With BFC</h2>
     <div class="container bfc">
         <div class="box"></div>
         <div class="box"></div>
     </div>
 </body>
 </html>
- 清除浮动
<!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Clear Float Example</title>
     <style>
         .container {
             width: 80%;
             margin: 0 auto;6
             background-color: lightgrey;
         }
 
         .box {
             width: 45%;
             float: left;
             margin: 10px;
             background-color: lightblue;
             height: 100px;
         }
 
         .clear {
             clear: both;
         }
 
         .container.bfc {
             overflow: hidden; /* Triggers BFC */
         }
     </style>
 </head>
 <body>
     <h2>Without BFC</h2>
     <div class="container">
         <div class="box">Box 1</div>
         <div class="box">Box 2</div>
     </div>
 
     <h2>With BFC</h2>
     <div class="container bfc">
         <div class="box">Box 1</div>
         <div class="box">Box 2</div>
     </div>
 </body>
 </html>
- 主要是防止高度塌陷
  • 浮动为什么会导致父元素高度塌陷

    • 浮动元素(通过 float 属性设置的元素)会脱离正常的文档流,不会影响其他非浮动元素的布局。这导致了一个常见的问题:父元素高度塌陷。这个现象的产生主要是因为浮动元素不再占据其原有的空间,父元素没有意识到它们的存在,因此父元素的高度无法包含这些浮动的子元素。

    • 解决方法

      • 将父元素的 overflow 属性设置为 hidden,触发 BFC

      • 在浮动元素后添加一个具有 clear: both; 的元素。

    	.clearfix::after {
            content: "";
            display: table;
            clear: both;
        }
    	```
    
  • 内存泄漏

      1. 被遗忘的定时器
      function startTimer() {
          setInterval(function() {
          }, 1000);
      }
      startTimer();
    
      1. dom界面没有删干净
      1. 闭包
      function outerFunction() {
          let bigData = new Array(1000000).fill('some data'); // 大量数据
          return function innerFunction() {
              // 使用大量数据
              console.log(bigData);
          }
      }
      const closure = outerFunction();
      // 如果内部函数形成了闭包,并且引用了外部函数的大量数据,这些数据将一直存在于内存中,即使外部函数执行完毕
    
      1. 意外创建的全局变量:意外创建的全局变量可能会一直存在于内存中,特别是在大型应用中,全局变量的数量可能会很大,如果不小心创建了很多全局变量并且忘记了清除,就可能导致内存泄漏。
      1. 旧版本浏览器 循环引用:在旧版本的浏览器中,循环引用可能会导致内存泄漏,因为旧版本的JavaScript引擎可能无法正确地处理循环引用,导致对象无法被垃圾回收器正确释放。
  • map和foreach

      • map 返回其原始数组的新数组,forEach却没有
      • 1.都是遍历
      • 2.都不改变原数组
      • 3.都接受一个回调函数
  • 数组去重

    • set
      const array = [1, 2, 2, 3, 4, 4, 5];
      const uniqueArray = [...new Set(array)];
      console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
    
    • filter
      const array = [1, 2, 2, 3, 4, 4, 5];
      const uniqueArray = array.filter((value, index, self) => self.indexOf(value) === index);
      console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
    
    • reduce
      const array = [1, 2, 2, 3, 4, 4, 5];
      const uniqueArray = array.reduce((acc, currentValue) => {
          if (!acc.includes(currentValue)) {
              acc.push(currentValue);
          }
          return acc;
      }, []);
      console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
    
  • 数组扁平化

    • 数组扁平化是指将多维数组转换为一维数组的过程。
    • flat()
  //使用 Infinity,可展开任意深度的嵌套数组
  var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
  arr4.flat(Infinity);
- 递归
  function flattenArray(arr) {
      return arr.reduce((acc, currentValue) => {
          if (Array.isArray(currentValue)) {
              return acc.concat(flattenArray(currentValue));
          } else {
              return acc.concat(currentValue);
          }
      }, []);
  }
  
  const nestedArray = [1, [2, [3, 4], 5], 6];
  const flatArray = flattenArray(nestedArray);
  console.log(flatArray); // 输出: [1, 2, 3, 4, 5, 6]
  • js模块化

    • 1.将一个复杂的程序依据一定的规范封装成几个块(文件),并进行组合在一起
    • 2.每个块的内部的数据与实现是私有的,只是向外暴露一些接口(方法)与外部其他模块通信
  • 避免变量名冲突(减少命名空间污染)

  • 更好的分离,按需加载 更高复用性 高可维护性

  • 匿名函数

    • 将匿名函数用圆括号包裹起来,然后紧接着使用另一对圆括号调用它。
  (function() {
      console.log("这是一个匿名函数自调用的示例。");
  })();
- 定义方法进行调用
  const iifeModule = (() => {
    let count = 0;
    return {
      increase: () => ++count;
      reset: () => {
        count = 0;
      }
    }
  })();
  
  iifeModule.increase();
  iifeModule.reset();
- 模块作为参数传递
  // logger 模块
  const logger = {
      log: function (message) {
          console.log(message);
      }
  };
  
  // 计算和的模块
  (function (logger) {
      function add(a, b) {
          logger.log(`计算 ${a} + ${b}`);
          return a + b;
      }
  
      const result = add(2, 3);
      logger.log(`结果是 ${result}`);
  })(logger);
  • 规范

    • CJS
      • CommonJS 规范的核心思想是每个文件都是一个模块,模块之间通过 require 函数来加载依赖,通过 module.exports 或 exports 来导出模块的接口。
  // 定义一个模块,模块名为 hello.js
  const message = "Hello, world!";
  function greet() {
      console.log(message);
  }
  
  // 导出模块的接口
  module.exports = {
      greet: greet
  };
  
  // main.js
  // 加载模块
  const hello = require('./hello');
  
  // 调用模块提供的接口
  hello.greet(); // 输出: Hello, world!
- AMD

	- 解决了在前端开发中按需加载和依赖管理的问题。
  define('amdModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1, dependencyModule2) => {
      // 业务逻辑
      // 处理部分
      let count = 0;
      const increase = () => ++count;
      const reset = () => {
          count = 0;
      };
  
      return {
          increase, reset
      };
  });
- CMD

	- 模块的加载是异步的,模块使用时才会加载。

- CMD 和 CommonJS、AMD 的比较

CommonJS:主要用于服务器端(如 Node.js),模块同步加载,适合 I/O 密集型操作。
AMD:主要用于浏览器端,模块提前加载,适合前端资源按需加载。
CMD:在浏览器端按需加载,依赖可以在需要时加载,加载是异步的,结合了 CommonJS 和 AMD 的优点,适合复杂的前端应用。

  • ES6模块化

    • 与CommonJS的差异:

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

  /** 定义模块 math.js **/
  var basicNum = 0;
  var add = function (a, b) {
      return a + b;
  };
  export { basicNum, add };
  /** 引用模块 **/
  import { basicNum, add } from './math';
  function test(ele) {
      ele.textContent = add(99 + basicNum);
  }
- 动态加载
  // 动态加载模块
  import('./module.js')
      .then((module) => {
          // 使用模块
          module.someFunction();
      })
      .catch((error) => {
          console.error('模块加载失败:', error);
      });
	- 应用场景

	- 按需加载:在单页面应用(SPA)中,根据用户的操作动态加载某些页面或组件,提高首屏加载速度和用户验。
    - 插件系统:在应用程序中实现插件系统,根据需要动态加载和运行插件。
    - 延迟加载:在某些资源密集型应用中,推迟加载某些模块,直到真正需要它们时再加载,以优化性能。
  • 继承与原型、原型链

    • 继承:通过原型链来实现继承

    • 原型:每一个方法都有一个原型,prototype

    • 原型链

      • 把对象的原型属性连成一条链
    • 代码

  function Person(name) {
      this.name = name;
  }
  Person.prototype.greet = function() {
      console.log(`Hello, my name is ${this.name}`);
  };
  function Student(name, studentID) {
      Person.call(this, name); // 调用 Person 构造函数以继承属性
      this.studentID = studentID;
  }
  // 设置 Student 的原型为 Person 的一个实例,以继承方法
  Student.prototype = Object.create(Person.prototype);
  Student.prototype.constructor = Student;
  Student.prototype.getID = function() {
      console.log(`My student ID is ${this.studentID}`);
  };
  const bob = new Student('Bob', '12345');
  bob.greet(); // 输出: Hello, my name is Bob
  bob.getID(); // 输出: My student ID is 12345
  • call、apply、bind区别

    • call

      • 调用函数并立即执行,逐个传递参数
  function greet(greeting, punctuation) {
      console.log(greeting + ', ' + this.name + punctuation);
  }
  
  const person = { name: 'Alice' };
  
  greet.call(person, 'Hello', '!'); // 输出: Hello, Alice!
- apply

	- 调用函数并立即执行,以数组形式传递参数
  function greet(greeting, punctuation) {
      console.log(greeting + ', ' + this.name + punctuation);
  }
  
  const person = { name: 'Alice' };
  
  greet.apply(person, ['Hello', '!']); // 输出: Hello, Alice!
- bind
	- 返回一个新的函数,不会立即执行,参数可以逐个传递或部分预设
  function greet(greeting, punctuation) {
      console.log(greeting + ', ' + this.name + punctuation);
  }
  
  const person = { name: 'Alice' };
  
  const greetAlice = greet.bind(person, 'Hello');
  greetAlice('!'); // 输出: Hello, Alice!
  • 深拷贝与浅拷贝

    • 深拷贝

      • 递归复制对象或数组的所有内容,包括嵌套的对象和数组。改变复制后的对象或数组不会影响到原对象或数组。

        • JSON
  const arr = [{ a: 1 }, { b: 2 }];
  const deepCopy = JSON.parse(JSON.stringify(arr));
			- 不能拷贝函数,不能拷贝特殊值

		- lodash
  const _ = require('lodash');
  
  const arr = [{ a: 1 }, { b: 2 }];
  const deepCopy = _.cloneDeep(arr);
		- 手写深拷贝
  function deepClone(obj) {
      if (obj == null) return null;
      if (obj instanceof RegExp) return new RegExp(obj);
      if (obj instanceof Date) return new Date(obj);
      if (typeof obj == "function") return new function (obj) { };
  
      if (typeof obj != "object") {
          return obj;
      }
      var newobj = new obj.__proto__.constructor;
      for (var key in obj) {
          newobj[key] = deepClone(obj[key]);
      }
      return newobj;
  }
- 浅拷贝

	- 复制对象或数组的引用,而不是内容。改变复制后的对象或数组会影响到原对象或数组。

		- slice
  const arr = [1, 2, 3];
  const shallowCopy = arr.slice();
		- concat
  const arr = [1, 2, 3];
  const shallowCopy = [].concat(arr);
		- map
  const arr = [1, 2, 3];
  const shallowCopy = arr.map(x => x);
		- 展开运算符
  const arr = [1, 2, 3];
  const shallowCopy = [...arr];
  • 数据类型判断

    • typeof:只能检查基本数据类型
  console.log(typeof 42); // "number"
  console.log(typeof 'Hello'); // "string"
  console.log(typeof true); // "boolean"
  console.log(typeof undefined); // "undefined"
  console.log(typeof null); // "object" (这是 JavaScript 的一个特殊现象)
  console.log(typeof {}); // "object"
  console.log(typeof []); // "object" (数组也是对象)
  console.log(typeof function() {}); // "function"
  console.log(typeof Symbol('sym')); // "symbol"
- instanceof:检测对象的具体类型
  console.log([] instanceof Array); // true
  console.log({} instanceof Object); // true
  console.log(new Date() instanceof Date); // true
  console.log(function() {} instanceof Function); // true
- Array.isArray:检测数组
  console.log(Array.isArray([])); // true
  console.log(Array.isArray({})); // false
- Object.prototype.toString.call:可以检测所有类型
  console.log(Object.prototype.toString.call(42)); // "[object Number]"
  console.log(Object.prototype.toString.call('Hello')); // "[object String]"
  console.log(Object.prototype.toString.call(true)); // "[object Boolean]"
  console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
  console.log(Object.prototype.toString.call(null)); // "[object Null]"
  console.log(Object.prototype.toString.call({})); // "[object Object]"
  console.log(Object.prototype.toString.call([])); // "[object Array]"
  console.log(Object.prototype.toString.call(function() {})); // "[object Function]"
  console.log(Object.prototype.toString.call(new Date())); // "[object Date]"
  console.log(Object.prototype.toString.call(/regex/)); // "[object RegExp]"
  console.log(Object.prototype.toString.call(new Map())); // "[object Map]"
  console.log(Object.prototype.toString.call(new Set())); // "[object Set]"
  console.log(Object.prototype.toString.call(Symbol('sym'))); // "[object Symbol]"
  • for in和of和each

    • for…in

      • 用于对象的属性遍历
    • for…of

      • 遍历数组或其他可迭代对象
    • forEach

      • 遍历数组的每一个元素
  const arr = [1, 2, 3];
  arr.forEach((value, index) => {
      console.log(index, value);
  });
  // 输出: 0 1, 1 2, 2 3
  • git版本回退

    • git reset

      • 用于本地未推送的更改,适用于需要彻底丢弃一些更改并清除提交历史的场景。
    • git revert

      • 适用于已经推送的更改,需要撤销某个特定提交但保持提交历史完整的场景。

        查看提交历史,找到要撤销的提交哈希

        git log --oneline

        反转指定的提交

        git revert f8e0abd

        推送更改到远程仓库

        git push origin main

  • z-index失效结果

    • 只能适用于整数

    • position 属性值是 static

    • 比较父元素的值

    • 堆叠上下文

React

  • 函数组件和类组件

    • 函数组件
  • 特点:
    1.无状态
    2.引入hooks,变的有状态
    3.性能好
    4.简单,不需要this

  import React, { useState, useEffect } from 'react';
		  
		  function MyComponent(props) {
		    const [count, setCount] = useState(0);
		  
		    useEffect(() => {
		      // 组件挂载时执行的副作用
		      document.title = `You clicked ${count} times`;
		  
		      // 组件卸载时执行的清理操作
		      return () => {
		        document.title = 'React App';
		      };
		    }, [count]);
		  
		    return (
		      <div>
		        <p>You clicked {count} times</p>
		        <button onClick={() => setCount(count + 1)}>Click me</button>
		      </div>
		    );
		  }
- 类组件
  • 1.代码复杂
    2.使用内置的状态和生命周期方法
    3.需要实例化和绑定this
    4.难以维护,可读性差
  import React, { Component } from 'react';
  
  class MyComponent extends Component {
    constructor(props) {
      super(props);
      this.state = { count: 0 };
    }
  
    componentDidMount() {
      document.title = `You clicked ${this.state.count} times`;
    }
  
    componentDidUpdate() {
      document.title = `You clicked ${this.state.count} times`;
    }
  
    componentWillUnmount() {
      document.title = 'React App';
    }
  
    handleClick = () => {
      this.setState({ count: this.state.count + 1 });
    }
  
    render() {
      return (
        <div>
          <p>You clicked {this.state.count} times</p>
          <button onClick={this.handleClick}>Click me</button>
        </div>
      );
    }
  }
  1. setState

  2. 受控组件和非受控组件

    • 受控组件

      • 通过setState将输入的值维护到了state中,需要时再从state中取出,这里的数据就收到了state的控制,称为受控组件。
    • 非受控组件

      • 页面中所有输入类的DOM如果是现用现取的称为非受控组件(用输入框内部的值是用户控制,和React无关)
  3. useMemo和useCallback

    • useMemo

      • 记忆化计算结果,避免在每次渲染时都进行昂贵的计算。

      • 返回的是一个值

      • 接受依赖项数组

    • useCallback

      • 记忆化回调函数,避免在每次渲染时都创建新的函数实例。

      • 返回的是一个函数

      • 接受依赖项数组

  4. axios的拦截

    • request

    1.添加JWT
    2.添加全局请求头
    3.添加日志记录
    4.验证是否登录
    5.防止重复请求

  • 实现方式

    • axios.interceptors.request.use()
    • URLSearchParams
  const url = 'https://example.com?foo=1&bar=2';
  const urlObj = new URL(url);
  const params = new URLSearchParams(urlObj.search);
  
  // 获取参数值
  console.log(params.get('foo')); // 1
  console.log(params.get('bar')); // 2
  
  // 添加新的参数
  params.append('baz', '3');
  console.log(params.toString()); // foo=1&bar=2&baz=3
  
  // 修改参数值
  params.set('foo', '42');
  console.log(params.toString()); // foo=42&bar=2&baz=3
  
  // 删除参数
  params.delete('bar');
  console.log(params.toString()); // foo=42&baz=3
  
  // 判断参数是否存在
  console.log(params.has('foo')); // true
  console.log(params.has('bar')); // false
- qs工具包

	- 对象和字符串互相转化
	- 处理嵌套对象,处理数组

- response

1.统一错误处理
2.统一数据格式,对后端友好
3.令牌过期,自送刷新令牌,请求重试

  • 有状态组件和无状态组件
    • 有状态组件
      • 有状态组件是可以维护和管理自身状态的组件
		import React, { Component } from 'react';
		  
		  class Counter extends Component {
		    constructor(props) {
		      super(props);
		      this.state = { count: 0 };
		    }
		  
		    increment = () => {
		      this.setState({ count: this.state.count + 1 });
		    };
		  
		    render() {
		      return (
		        <div>
		          <p>Count: {this.state.count}</p>
		          <button onClick={this.increment}>Increment</button>
		        </div>
		      );
		    }
		  }
		  
		  // 使用有状态组件
		  const App = () => {
		    return <Counter />;
		  };
		  
		  export default App;
  • 理解
    - 是类组件
    - 有继承
    - 可以使用this
    - 可以使用react生命周期
    - 使用较多,容易频繁出发生命周期钩子函数,影响性能
    - 内部使用state,维护自身状态的变化,有状态组件根据外部组件传入的props和自身的state进行渲染。

  • 无状态组件

    • 它们只依赖于传入的 props 来渲染内容
  // 无状态函数组件
  const Greeting = ({ name }) => {
    return <h1>Hello, {name}!</h1>;
  };
  
  // 使用无状态组件
  const App = () => {
    return <Greeting name="Alice" />;
  };
  
  export default App;
  • 理解
    - 不依赖自身的状态state
    - 可以是类组件或者函数组件
    - 可以完全避免使用this关键字。(由于使用的是箭头函数事件无需绑定)
    - 有更高的性能。当不需要使用生命周期钩子时,应该首先使用无状态函数组件
    - 组件内部不维护state,可以根据外部组件传入的props进行渲染,当props改变时,组件重新渲染

    • 优点和缺点

      • 优点

        • 简化代码
        • 组件不需要被实例化,无生命周期,提高性能。输出(渲染)只取决于输入(属性),无副作用
        • 视图和数据的解耦分离
      • 缺点

        • 无法使用ref
        • 无生命周期方法
        • 无法控制组件的重新渲染,因为无法使用shouldComponentUpdate方法,当组件接收到新的属性时,会重新渲染
  • Hooks在平时开发中需要注意的问题

      - useState在设置状态时,只有第一次是生效的,后期需要更新状态,必须通过useEffect
      - 在函数组件中使用 useState 定义状态时,状态的更新不会触发组件的重新渲染。这是因为 React 在函数组件中不会自动追踪状态的变化,所以需要使用 useEffect 来处理状态更新后的副作用,并在其中执行需要在状态变化时触发的操作。
    
  import React, { useState, useEffect } from 'react';
  
  const Counter = () => {
    const [count, setCount] = useState(0);
  
    // 在 useEffect 中监听 count 的变化,并执行副作用
    useEffect(() => {
      console.log('Count has changed:', count);
      // 这里可以执行需要在 count 变化时触发的操作
    }, [count]); // 指定依赖为 count,只有 count 发生变化时才会触发 useEffect
  
    const increment = () => {
      setCount(count + 1); // 更新 count 的值
    };
  
    return (
      <div>
        <p>Count: {count}</p>
        <button onClick={increment}>Increment</button>
      </div>
    );
  };
  
  export default Counter;
- 不要在循环、条件或嵌套函数中调用hook,必须始终在React函数的顶层使用Hook
- 这是因为React需要利用调用顺序来正确更新相应的状态,以及相应的钩子函数,一旦在循环条件或条件分支语句中调用Hook,就容易导致调用的顺序不一致性,从而产生难以预料的后果。

React生命周期

React组件生命周期方法在不同的阶段有不同的使用场景和用途。以下是更具体的说明:

挂载阶段 (Mounting Phase)

  1. constructor(props)
    • 作用:初始化组件的状态(state)和绑定事件处理程序。
    • 使用场景:设置初始状态和绑定方法。
    • 示例
   constructor(props) {
     super(props);
     this.state = {
       count: 0
     };
     this.handleClick = this.handleClick.bind(this);
   }
  1. static getDerivedStateFromProps(nextProps, prevState)
    • 作用:根据传入的props更新state。此方法必须是静态方法,不能访问this。
    • 使用场景:当state需要根据props变化时使用。
    • 示例
   static getDerivedStateFromProps(nextProps, prevState) {
     if (nextProps.value !== prevState.value) {
       return {
         value: nextProps.value
       };
     }
     return null;
   }
  1. render()
    • 作用:返回要渲染的React元素。此方法是纯函数,不应包含任何副作用。
    • 使用场景:定义组件的UI。
    • 示例
   render() {
     return <div>{this.state.value}</div>;
   }
  1. componentDidMount()
    • 作用:组件已经被渲染到DOM中,可以在此进行DOM操作或数据请求。
    • 使用场景:初始化数据请求、设置订阅、操作DOM等。
    • 示例
   componentDidMount() {
     fetch('https://api.example.com/data')
       .then(response => response.json())
       .then(data => this.setState({ data }));
   }

更新阶段 (Updating Phase)

  1. static getDerivedStateFromProps(nextProps, prevState)

    • 作用:同挂载阶段,根据传入的props更新state。
    • 使用场景:当组件在更新过程中需要根据新的props更新state时。
  2. shouldComponentUpdate(nextProps, nextState)

    • 作用:决定是否需要重新渲染组件。默认返回true。
    • 使用场景:优化性能,防止不必要的渲染。
    • 示例
   shouldComponentUpdate(nextProps, nextState) {
     return nextProps.value !== this.props.value || nextState.value !== this.state.value;
   }
  1. render()

    • 作用:返回需要渲染的React元素。同挂载阶段。
  2. getSnapshotBeforeUpdate(prevProps, prevState)

    • 作用:在DOM更新前捕获一些信息。返回的值会作为componentDidUpdate的第三个参数。
    • 使用场景:在DOM变更前获取一些信息(例如滚动位置)。
    • 示例
   getSnapshotBeforeUpdate(prevProps, prevState) {
     if (prevProps.list.length < this.props.list.length) {
       return this.listRef.scrollHeight;
     }
     return null;
   }
  1. componentDidUpdate(prevProps, prevState, snapshot)
    • 作用:组件更新后被调用。可以进行DOM操作或数据请求。
    • 使用场景:基于更新后的DOM进行操作。
    • 示例
   componentDidUpdate(prevProps, prevState, snapshot) {
     if (snapshot !== null) {
       this.listRef.scrollTop = this.listRef.scrollHeight - snapshot;
     }
   }

卸载阶段 (Unmounting Phase)

  1. componentWillUnmount()
    • 作用:组件即将从DOM中移除。在这里可以执行清理操作,例如清除定时器、取消网络请求或清理订阅等。
    • 使用场景:释放资源,防止内存泄漏。
    • 示例
   componentWillUnmount() {
     clearInterval(this.timerID);
   }

通过正确使用这些生命周期方法,可以更好地控制组件的行为,提高应用的性能和用户体验。

- 挂载阶段(Mounting):

组件第一次在DOM树中被渲染的过程

	- constructor
	- 组件的构造函数,第一个被执行,若没有显示定义它,会有一个默认的构造函数,但是若显示定义了构造函数,必须在构造函数中执行super(props),否则无法在构造函数中拿到this。
  constructor(props) {
    super(props);
    // 不要在构造函数中调用 setState,可以直接给 state 设置初始值
    this.state = { counter: 0 }
    this.handleClick = this.handleClick.bind(this)
  }
	- 初始化组件的state
	- 给事件处理方法绑定this
	- static getDerivedStateFromProps
	- 这是个静态方法,不能在这个函数里使用this,有两个参数props和state,分别指接受到的新参数和当前组件的state对象。这个函数会返回一个对象来更新当前的state对象,如果不需要可以返回null。
render
  • 返回要渲染的React元素。此方法是纯函数,不应包含任何副作用。
  • componentDidMount
  • 组件已经被渲染到DOM中,可以在此进行DOM操作或发送网络请求。
  • 在浏览器刷新屏幕前执行
更新阶段(Updating):
	组件状态发生变化,重新更新渲染的过程

	- static getDerivedStateFromProps

	- shouldComponentUpdate

		- 指示react是否要重新渲染该组件,通过返回true和fasle来指定

	- render

	- getSnapshotBeforeUpdate

		- 执行在render之后,componentDidUpdate之前

		- 该函数的返回值,会作为componentDidUpdate的第三个参数

	- componentDidUpdate

		- 组件更新后被调用
卸载阶段(Unmounting):

组件从DOM树中被移除的过程

	- componentWillUnmount

		- 清除 timer,取消网络请求或清除

		- 取消在 componentDidMount() 中创建的订阅等,

注意:不要使用setState,因为组件一旦卸载,就不会装载也不会渲染

cookie和session

cookie
	-  Cookie 是存储在客户端(通常是浏览器)中的小块数据,由服务器生成并发送给客户端,客户端会在后续请求中携带这些 Cookie 数据。
	- 存储位置: 存储在客户端浏览器中。
	- 生命周期: 可以设置过期时间(Expires 或 Max-Age 属性),过期后会自动删除。如果不设置过期时间,Cookie 会在会话结束(浏览器关闭)时删除。
	- 大小限制: 每个 Cookie 的大小限制约为 4KB,每个域名下的 Cookie 总数有一定限制(通常是 20 个左右)。
	- 用途: 常用于会话管理、个性化设置、跟踪用户行为等。
	- 安全性: 可以通过设置 HttpOnly 属性,防止客户端脚本访问 Cookie,增加安全性;通过设置 Secure 属性,确保 Cookie 只在 HTTPS 连接上传输。
session
	- Session 是存储在服务器上的数据,用于跟踪用户的会话状态。每个会话通常都有一个唯一的会话 ID(Session ID),该 ID 会存储在客户端的 Cookie 中,并在每次请求时发送给服务器。
	- 存储位置: 存储在服务器上。
	- 生命周期: 由服务器管理,可以设置会话过期时间。默认情况下,会话在用户关闭浏览器或超时时结束。
	- 大小限制: 由服务器内存或存储机制决定,通常没有单个会话数据的大小限制。
	- 用途: 常用于身份验证、购物车、用户偏好等需要在多次请求间保持状态的数据。
	- 安全性: 数据存储在服务器上,相对较为安全,客户端无法直接修改 Session 数据。

编程架构

MVC

当用户与页面交互, View 需要更新时,首先去找 Controller,然后 Controller 通过调用Model层,来完成对Model层的修改,然后Model层再去通知VIew层更新。

MVVM

Model和View并无直接联系,而是通过ViewModel来进行联系的,Model层和VIeModel层有着双向数据绑定的联系,因此Model中的数据改变时会触发View的刷新,用户在view层操作而改变的数据也会在Model层同步。

MVP

view需要更新数据时,首先去找Presenter,然后Presenter去找Model请求数据,Model获取到数据之后通知Presenter,Presenter再通知view更新。

useeffect 代替了哪些生命周期
- componentDidMount             组件挂载后
- componentDidUpdate            组件更新后
- componentWillUnmount        组件卸载后
react合成事件
  • 事件冒泡
    在Web开发中,事件冒泡(Event Bubbling)是指当一个事件被触发时,它会从最具体的目标元素开始,逐级向上传播到祖先元素,直到到达根元素(通常是document对象)。这是浏览器处理事件的一种机制。

  • 如何阻止?

    • 好处

      • 跨平台
      • 避免垃圾回收
      • 引入事件池机制
      • 事件存在一个数组中,用的时候拿出来
      • 方便事件统一管理
    • 与原生事件的区别

      • 原生事件:onclick
        React事件:onClick

      • 原生事件:返回false来阻止默认行为
        React事件:调用preventDefaullt()

Vue

  • 路由守卫

    • 全局守卫

      • 全局前置守卫 (beforeEach):在每次导航前触发。
      • 全局解析守卫 (beforeResolve):在每次导航被确认之前,所有组件内守卫和异步路由组件被解析之后触发。
      • 全局后置守卫 (afterEach):在每次导航结束后触发。
    • 路由独享守卫

      • 路由独享守卫 (beforeEnter):在进入该路由前触发。
    • 组件内守卫

      • beforeRouteEnter:在路由进入前调用
      • beforeRouteUpdate:在当前路由改变,但该组件被复用时调用。
      • beforeRouteLeave:导航离开该组件的路由时调用。
    • 场景

      • 认证和授权:在导航前检查用户是否已登录,或者是否具有访问某些路由的权限。
      • 数据获取:在进入路由前获取必要的数据,确保组件渲染时已经有数据。
      • 动态标题:根据路由变化动态更改页面标题。
      • 防止未保存的更改丢失:在用户导航离开当前页面前提示保存未保存的更改。
  • 正则

    • 过滤数字
  let a = '123efw4k5';
  let filteredString = a.replace(/[^a-zA-Z]/g, '');
  console.log(filteredString); // 输出:efwk
基本语法
  • keep-alive

    • 概要

      • 用于缓存动态组件,以防止它们被销毁,从而保留它们的状态或避免重新渲染。
    • 属性

      • include:字符串或正则表达式,只有匹配的组件会被缓存。
      • exclude:字符串或正则表达式,任何匹配的组件都不会被缓存。
      • max:数字,最多可以缓存多少组件实例。
    • 钩子触发

      • activated:组件从缓存中激活时调用。
      • deactivated:组件被缓存时调用。
    • 场景

      • 表单数据保留:当用户在表单中填写数据时,切换到其他页面再切换回来,表单数据不会丢失。
      • 分页数据保存:在列表分页中,用户查看某一页的数据,切换到其他页面再切换回来,分页数据不会重新加载。
      • 多标签页切换:在多标签页应用中,切换标签时保持每个标签页的状态。
  • cookie、localstorage、sessionstorage

    • 同源策略

      • 协议,域名,端口都要相同,其中有一个不同都会产生跨域;
    • cookie

      • 介绍

      • 存储大小

      • 性能

      • 时效性

    • localstorage

    • sessionstorage

  • 其它

    • 发布订阅者模式

      • 代码
  class PubSub {
    constructor() {
      this.subscribers = {};
    }
  
    subscribe(topic, callback) {
      if (!this.subscribers[topic]) {
        this.subscribers[topic] = [];
      }
      this.subscribers[topic].push(callback);
    }
  
    publish(topic, data) {
      if (!this.subscribers[topic]) return;
      this.subscribers[topic].forEach(callback => callback(data));
    }
  
    unsubscribe(topic, callback) {
      if (!this.subscribers[topic]) return;
      this.subscribers[topic] = this.subscribers[topic].filter(cb => cb !== callback);
    }
  }
  
  // 创建一个发布订阅实例
  const pubSub = new PubSub();
  
  // 订阅一个主题
  pubSub.subscribe('news', data => console.log(`新闻订阅者1收到: ${data}`));
  pubSub.subscribe('news', data => console.log(`新闻订阅者2收到: ${data}`));
  
  // 发布一个主题
  pubSub.publish('news', '今天的头条新闻!');
  
  // 取消订阅
  const callback = data => console.log(`新闻订阅者3收到: ${data}`);
  pubSub.subscribe('news', callback);
  pubSub.unsubscribe('news', callback);
  pubSub.publish('news', '更多的新闻内容!');
- webpack的优化插件

	- TerserPlugin

		- 压缩JS

	- MiniCssExtractPlugin

		- 把css单独提取到一个文件中

	- OptimizeCSSAssetsPlugin

		- 压缩CSS

	- CompressionWebpackPlugin

		- 压缩生产版本的文件

-  scss、less、css区别

	- css
	- scss
  $primary-color: #333;
  
  body {
    font: 100% Helvetica, sans-serif;
    color: $primary-color;
  }
  
  nav {
    ul {
      margin: 0;
      padding: 0;
      list-style: none;
    }
  
    li { display: inline-block; }
  
    a {
      font-weight: bold;
      text-decoration: none;
      &:hover {
        color: $primary-color;
      }
    }
  }
- 变量:支持变量,允许在整个样式表中重复使用相同的值。
- 嵌套规则:允许嵌套 CSS 选择器,更加直观和结构化。
- 混合(Mixins):允许创建可重用的样式块。
- 继承(Extend/Inheritance):允许一个选择器继承另一个选择器的样式。
- 运算:支持数学运算,可以直接在样式表中进行计算。
- 条件和循环:支持基本的编程结构,如条件语句和循环。
  • less

    • MVVM、MVC的区别

    • 哪些方法能够处理异步

      • 回调函数:简单直接,但可能导致回调地狱。
      • Promise:避免回调地狱,支持链式调用和统一错误处理。
      • async/await:Promise 的语法糖,使异步代码更加简洁。
      • 事件驱动:适合处理多事件场景,使用事件监听和触发机制。
      • RxJS:功能强大的库,用于处理复杂的异步数据流。
    • 跨域怎么解决

      • cors
      • jsonp
      • 代理服务器
      • nginx反向代理
        • Access-Control-Allow-Origin
      • WebSocket
      • postMessage
浏览器缓存

http://t.csdnimg.cn/S1oe8

  • 场景

    • this.$nextTick
      • 确保你在数据变化引起的 DOM 更新完成后执行某些操作。
    • @blur 事件
      • 绑定在输入框上,当输入框失去焦点时触发 handleBlur 方法。

Other

防抖

防抖就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

function debounce(fn, delay) {
    let timer = null;
    return function (...args) { 
        clearTimeout(timer);
        timer = setTimeout(() => {
            fn.apply(this, args); // 将接收到的参数传递给原始函数 fn
        }, delay);
    };
}
ft = (a, b) => {
    console.log(a + b);
};
const debouncedFn = debounce(ft, 1000); // 将 debounce 返回的函数赋值给一个变量
debouncedFn(1, 2); // 调用 debouncedFn 来触发防抖处理
  • 节流

    • // 节流就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率
function throttle(fn, delay) {
    let canRun = true;
    return function () {
        if (!canRun) return;
        canRun = false;
        setTimeout(() => {
            fn.apply(this, arguments);
            canRun = true;
        }, delay);
    };
}

箭头函数和普通函数

// 箭头函数和普通函数的区别
// 1.箭头函数没有 this,this 指向是定义时的外层第一个普通函数的 this
// 2.箭头函数没有 arguments
// 3.箭头函数没有 prototype
// 4.箭头函数不能作为构造函数

// 举一个带有对比性的例子
// 普通函数

function foo() {
    console.log(this);
}
foo(); // window

// 箭头函数

const bar = () => {
    console.log(this);
};
bar();

// 什么时候使用箭头函数 什么时候使用普通函数
// 1.箭头函数适合在不需要 this 的情况下使用
// 2.普通函数适合在需要 this 的情况下使用
// 3.箭头函数适合在需要 arguments 的情况下使用
// 4.普通函数适合在需要 prototype 的情况下使用
// 5.箭头函数适合在不需要 new 的情况下使用
// 6.普通函数适合在需要 new 的情况下使用

  • 闭包

  • // 什么是闭包
    // 闭包是指有权访问另一个函数作用域中的变量的函数

// 闭包的特性:
// 1.函数嵌套函数
// 2.函数内部可以引用外部的参数和变量
// 3.参数和变量不会被垃圾回收机制回收

// 实现闭包

函数执行后返回的结果是一个内部函数,并被外部变量所引用,如果内部函数持有被执行函数作用域的变量,则形成了闭包。

function outer() {
    let a = 1;
    return function inner() {
        console.log(a);
    };
}
  • 原型链

  • / 什么是原型链
    // 原型链是 JavaScript 中实现继承的一种模式,每个对象都有一个原型对象,对象可以通过原型对象共享方法和属性
    // 每个对象都有一个原型对象,原型对象也是对象,所以原型对象也有原型对象,这样就形成了一个链式结构,即原型链
    // 原型链的终点是 null,null 没有原型对象

  • es6新特性

// es6新特性
// let 和 const
// let 和 const 是 es6 新增的两个声明变量的关键字
// let 用于声明变量,const 用于声明常量
// let 和 const 都是块级作用域
// let 和 const 都不会变量提升
// let 和 const 都不允许重复声明
// const 声明的常量是不可变的,但是如果常量是对象或数组,对象和数组的属性或元素是可以修改的

// 模板字符串
// 模板字符串是一种特殊的字符串,可以在其中插入变量和表达式
// 模板字符串使用反引号 来定义,变量和表达式使用 ${} 来插入 const name = "Alice"; const age = 20; const greeting =Hello, my name is ${name}, I’m ${age} years old.`;
console.log(greeting); // Hello, my name is Alice, I’m 20 years old.

// 箭头函数
// 箭头函数是一种新的函数声明方式,使用箭头符号 => 来定义函数
// 箭头函数没有 this,this 指向是定义时的外层第一个普通函数的 this
// 箭头函数没有 arguments
// 箭头函数没有 prototype
// 箭头函数不能作为构造函数
const add = (a, b) => a + b;
console.log(add(1, 2)); // 3

  • Promise

  • // Promise
    // Promise 是一种异步编程的解决方案,用于处理异步操作
    // Promise 有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
    // Promise 的状态一旦改变,就不会再变
    // Promise 的 then() 方法用于注册回调函数,catch() 方法用于捕获错误
    // Promise 的 all() 方法用于将多个 Promise 实例包装成一个新的 Promise 实例

// Promise
// 创建一个 Promise 对象

const myPromise = new Promise((resolve, reject) => {
    // 这里是异步操作,比如从服务器获取数据
    // 如果操作成功,调用 resolve() 并传递结果
    // setTimeout(() => {
    //     const data = { message: "Data received successfully" };
    //     resolve(data);
    // }, 2000); // 假设操作需要2秒完成

    // 如果操作失败,调用 reject() 并传递错误信息
    setTimeout(() => {
        reject("Error: Unable to fetch data");
    }, 2000); // 假设操作失败也需要2秒完成
});

// 使用 Promise 对象

myPromise
    .then((data) => {
        // 在操作成功时执行的代码
        console.log("Success:", data.message);
    })
    .catch((error) => {
        // 在操作失败时执行的代码
        console.error("Error:", error);
    });
同步和异步

async/await
// async/await 是 es8 新增的异步编程解决方案,基于 Promise 实现
// async 用于声明一个函数是异步的,返回值是 Promise 对象
// await 用于等待 Promise 对象的状态变为 fulfilled,然后获取 Promise 对象的结果
// async/await 可以让异步代码像同步代码一样直观易读

  • 解构赋值

    • // 解构赋值
      // 解构赋值是一种 JavaScript 表达式,可以将数组或对象中的数据解析出来赋值给变量
      // 数组解构赋值
      const arr = [1, 2, 3];
      const [a, b, c] = arr;
      console.log(a, b, c); // 1 2 3
  • 看代码写结果

    • for (var i = 0; i < 5; i++) {
      setTimeout(function () {
      console.log(i);
      }, 1000);
      }

// 正常输出

for (var i = 0; i < 5; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i);
    }, 1000);
  })(i);
}
  • 手写 LRU

优化

回流与重绘

回流

当渲染树中部分或者全部元素的尺寸、结构或者属性发生变化时,浏览器会重新渲染部分或者全部文档的过程就称为回流。

		- 页面的首次渲染
		- 浏览器窗口大小发生变化
		- 元素内容发生变化
		- 元素的字体大小发生变化
		- 元素的尺寸或者位置发生变化
		- 激活css伪类
		- 添加或删除可见的DOM元素

- 重绘

	- 当页面中某些元素的样式发生变化,但是不会影响其在文档流中的位置时,浏览器就会对元素进行重新绘制,这个过程就是重绘。

		- color、background相关属性:background-color、background-image等

outline相关属性:outLine-color、outline-width、text-decoration等
border-radius、visibility、boxshadow

- 如何避免

	- 操作DOM时,尽量在底层级的DOM节点进行操作
	- 减少使用css表达式的频率
	- 不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式
	- 使用absolute或者fixed,使元素脱离文档流,这样他们发生变化不会影响其他元素
	- 避免频繁操作DOM,可以创建一个文档片段documentFragment,在它上面应用所有DOM操作,最后把它添加到文档中
	- 将元素先设置display:none,操作结束后再把它显示出来。因为display属性为none的元素上进行DOM操作不会引发回流和重绘
	- 将多个DOM操作写在一起,而不是读写操作穿插着写,这得益于浏览器的渲染队列机制
  • Webpack

  • 懒加载

    • 图片的加载是由src引起的,当对src赋值时,浏览器就会请求图片资源。根据这个原理,将页面上的图片的src属性设置为空字符串,将图片的真实路径保存在一个自定义属性中,当页面滚动时,进行判断,如果图片进入可视区,则从自定义属性中取出真实路径赋值给图片的src属性,以此来实现图片的延迟加载。
  <div class="container">
       <img src="loading.gif"  data-src="pic.png">
       <img src="loading.gif"  data-src="pic.png">
       <img src="loading.gif"  data-src="pic.png">
       <img src="loading.gif"  data-src="pic.png">
       <img src="loading.gif"  data-src="pic.png">
       <img src="loading.gif"  data-src="pic.png">
  </div>
  <script>
  var imgs = document.querySelectorAll('img');
  function lozyLoad(){
  		var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  		var winHeight= window.innerHeight;
  		for(var i=0;i < imgs.length;i++){
  			if(imgs[i].offsetTop < scrollTop + winHeight ){
  				imgs[i].src = imgs[i].getAttribute('data-src');
  			}
  		}
  	}
    window.onscroll = lozyLoad();
  </script>

在这里插入图片描述

  • window.innerHeight 是浏览器可视区的高度
  • document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的过的距离
  • imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离)
  • 图片加载条件:img.offsetTop < window.innerHeight + document.body.scrollTop;

计网

  • 在浏览器输入地址后,按下回车键,都发生了什么?

    • 浏览器自动补全协议、端口
    • 浏览器自动完成url编码
    • 浏览器根据url地址查找本地缓存,根据缓存规则看是否命中缓存,若命中缓存则直接使用缓存,不再发出请求
    • 通过DNS解析找到服务器的IP地址
    • 浏览器向服务器发出建立TCP连接的申请,完成三次握手后,连接通道建立
    • 若使用了HTTPS协议,则还会进行SSL握手,建立加密信道。使用SSL握手时,会确定是否使用HTTP2
    • 浏览器决定要附带哪些cookie到请求头中
    • 浏览器自动设置好请求头、协议版本、cookie,发出GET请求
    • 服务器处理请求,进入后端处理流程。完成处理后,服务器响应一个HTTP报文给浏览器。
    • 浏览器根据使用的协议版本,以及Connection字段的约定,决定是否要保留TCP连接。
    • 浏览器根据响应状态码决定如何处理这一次响应
    • 浏览器根据响应头中的Content-Type字段识别响应类型,如果是text/html,则对响应体的内容进行HTML解析,否则做其他处理
    • 浏览器根据响应头的其他内容完成缓存、cookie的设置
    • 浏览器开始从上到下解析HTML,若遇到外部资源链接,则进一步请求资源
    • 解析过程中生成DOM树、CSSOM树,然后一边生成,一边把二者合并为渲染树(rendering tree),随后对渲染树中的每个节点计算位置和大小(reflow),最后把每个节点利用GPU绘制到屏幕(repaint)
    • 在解析过程中还会触发一系列的事件,当DOM树完成后会触发DOMContentLoaded事件,当所有资源加载完毕后会触发load事件
  • https与http

    • http

      • 无状态性:每个请求都是独立的,服务器不会保留任何关于客户端状态的信息。这意味着每个请求都是相互独立的,服务器无法确定两个请求是否来自同一个客户端。

        • 无状态性实现方式

          • URL参数:客户端将会话状态信息作为URL参数传递给服务器,服务器根据这些参数来生成响应。
          • Cookie:客户端使用Cookie来保存会话状态信息,并在每个请求中将Cookie发送给服务器,服务器根据Cookie来识别客户端并生成响应。
          • Token:客户端使用Token来保存会话状态信息,并在每个请求中将Token发送给服务器,服务器根据Token来识别客户端并生成响应。
    • 明文传输:HTTP数据在传输过程中是未加密的,因此容易受到中间人攻击或窃听。

    • https

      • 加密传输:HTTPS使用SSL/TLS协议对数据进行加密,使得数据在传输过程中不易被窃取或篡改。这样可以保护用户的隐私和数据安全。
      • 身份验证:HTTPS使用SSL证书对服务器进行身份验证,确保客户端连接到的是真实的服务器,而不是中间人攻击。
  • UDP 与 TCP

    • TCP

      • 连接导向:在发送数据之前需要建立连接(三次握手),在结束数据传输时需要释放连接(四次挥手)。

        • 三次握手

          • 客户端发送SYN,服务器回复SYN-ACK,客户端再发送ACK。
    • 2次握手
      - 如果客户端没有收到服务器的SYN-ACK包(例如网络问题或包丢失),客户端会继续等待,而服务器认为连接已经建立,导致资源浪费。三次握手通过第三个ACK包解决了这个问题,确保客户端和服务器都确认连接的建立。

    • 四次挥手
      - 客户端发送FIN,服务器回复ACK,服务器再发送FIN,客户端回复ACK。
      - 三次挥手

        	- 如果没有最后一步:在这种情况下,客户端不知道服务器是否已经收到它的ACK包。服务器也不知道客户端是否已经收到它的FIN包,这可能导致资源的浪费和连接不完全关闭。四次挥手通过第四个ACK包解决了这个问题,确保客户端和服务器都确认连接的关闭。
      
      • 可靠传输:通过确认机制、超时重传和校验和保证数据的可靠传输。

      • 有序传输:数据包按顺序接收,接收端会按顺序重组数据。

        • 序列号

        • 确认机制

        • 接收方接收到数据后会告知发送方哪些数据被接收

        • 重传机制

        • 流量控制:通过滑动窗口机制控制数据传输速率,防止网络拥塞。

        • 滑动窗口:发送方和接收方各维护一个窗口,表示可以发送和接收的数据范围。窗口大小动态调整,取决于接收方的接收能力。

        • 窗口缩小和扩大:接收方通过ACK消息中的窗口大小(Window Size)告知发送方其接收能力。发送方根据该信息调整数据发送速度。

      • 拥塞控制:使用拥塞避免算法(如慢启动、拥塞避免、快速重传和快速恢复)控制发送数据的速率。

        • 慢启动(Slow Start):发送方初始发送速率较低,并逐渐增大发送速率,观察网络的反应。当检测到网络拥塞时(通过丢包或延时增加),减小发送速率。

        • 拥塞避免(Congestion Avoidance):当网络接近饱和时,发送方缓慢增加发送速率,防止网络进入拥塞状态。

        • 快速重传和快速恢复(Fast Retransmit and Fast Recovery):检测到数据包丢失(通过重复ACK)后,发送方立即重传丢失的数据包,并调整发送速率。

      • 考点

        • 为何不能合并服务器的ACK和FIN成三次挥手?

        • 服务器可能还有数据未发送。先回复ACK表示接收到关闭请求,再发送FIN断开服务器到客户端的数据传输。

        • 若服务器的ACK在第二次挥手时未送达,会怎样?

        • 客户端未收到确认,会重新发送FIN请求。

      • 为何第四次挥手客户端需等待2*MSL?

        • 防止服务器未收到ACK,重发FIN。客户端在2*MSL内收到FIN后,重新发送ACK并等待2MSL,确保服务器收到ACK。
    • UDP

      • 无连接:发送数据前不需要建立连接,数据包独立发送。
      • 不可靠传输:不保证数据包的顺序和完整性,没有确认机制和重传机制。
      • 轻量级:头部开销小(8字节),适合高性能要求的应用。
      • 无序传输:数据包可能乱序到达,需要应用层自行处理顺序。
      • 无流量控制:发送速率不受限制,可能导致网络拥塞。
      • 适用场景:适用于对传输速度要求高且对数据完整性要求低的应用,如实时视频/音频传输(如VoIP)、在线游戏、DNS查询等。
http1.0 /http1.1/http2.0
- HTTP/1.0:每个请求都需要新建一个连接,缺乏持久连接和有效的缓存控制。

- HTTP/1.1:引入了持久连接、管道化、分块传输编码和更丰富的缓存控制机制,显著提高了传输效率。
	- 持久连接:减少了连接建立和断开的开销,提高了传输效率。
	- 管道化(Pipelining):允许在同一个连接上同时发送多个请求,而不必等待前一个请求的响应(不过管道化在实际使用中并不常见,很多服务器和代理不支持)。
	- 分块传输编码(Chunked Transfer Encoding):允许服务器分块发送响应数据,适用于动态生成内容的场景。
	- 更多缓存控制头字段:例如Cache-Control,提供了更细粒度的缓存控制。
	- 额外的状态码和头字段:如100(Continue)状态码,Host头字段(支持虚拟主机)。

- HTTP/2.0:通过多路复用、二进制分帧、头部压缩和服务器推送等特性,大幅提高了传输效率和并行处理能力,解决了HTTP/1.x中的许多性能瓶颈。
	- 多路复用:在一个TCP连接上可以并行处理多个请求和响应,消除了HTTP/1.x中存在的队头阻塞问题。
	- 二进制分帧层:使用二进制格式进行传输,取代了HTTP/1.x的文本格式,提高了传输效率和解析速度。
	- 头部压缩:使用HPACK压缩算法对头部信息进行压缩,减少了冗余数据,提高了传输效率。
	- 服务器推送(Server Push):服务器可以在客户端请求之前主动推送资源到客户端,减少了延迟。

状态码

- 1xx:信息响应
	- 100 Continue:表示服务器已经收到请求头,并且客户端应继续发送请求正文(在发送大请求正文之前,可以先发送这个请求头以检查服务器是否允许)。
	- 101 Switching Protocols:表示服务器正在根据客户端的请求切换协议(例如,切换到WebSocket)。

- 2xx:成功响应
	- 200 OK:请求成功。服务器已成功处理请求,并返回了请求的资源。
	- 201 Created:请求成功,并且服务器创建了一个新的资源。
	- 202 Accepted:请求已接受,但尚未处理(这意味着处理可能还未完成)。
	- 204 No Content:服务器成功处理了请求,但没有返回任何内容。

- 3xx:重定向响应
	- 301 Moved Permanently:请求的资源已永久移动到新的URL,客户端应使用新的URL进行访问。
	- 302 Found(或Moved Temporarily):请求的资源临时移动到新的URL,客户端应使用原始URL进行访问。
	- 303 See Other:请求的资源可以在另一个URL找到,客户端应使用GET方法请求新的URL。
	- 304 Not Modified:请求的资源未修改,客户端可以使用缓存的版本。

- 4xx:客户端错误
	- 400 Bad Request:服务器无法理解请求的格式,客户端应修改请求后重新发送。
	- 401 Unauthorized:请求未授权,客户端必须进行身份验证后才能访问资源。
	- 403 Forbidden:服务器拒绝请求,即使客户端进行了身份验证也无权访问资源。
	- 404 Not Found:服务器找不到请求的资源。
	- 405 Method Not Allowed:请求方法(如GET、POST)不被服务器允许。

- 5xx:服务器错误
	- 500 Internal Server Error:服务器遇到未知错误,无法处理请求。
	- 501 Not Implemented:服务器不支持请求的方法。
	- 502 Bad Gateway:服务器作为网关或代理,从上游服务器收到无效响应。
	- 503 Service Unavailable:服务器目前无法处理请求,通常是因为服务器过载或维护。
	- 504 Gateway Timeout:服务器作为网关或代理,未能及时从上游服务器收到响应。

面经

4. Webpack和Vite的区别(优缺点)

Webpack:

  • 优点:
    • 功能强大,插件和loader生态丰富,能够处理复杂的构建需求。
    • 广泛使用,社区支持和资源丰富。
    • 配置灵活,能够自定义构建流程。
  • 缺点:
    • 配置复杂,学习曲线较陡。
    • 构建速度较慢,尤其是大型项目。

Vite:

  • 优点:
    • 开发服务器启动速度快,利用原生ES模块实现快速热更新。
    • 构建速度快,利用Rollup进行生产构建。
    • 配置简单,默认配置即可满足大部分需求。
  • 缺点:
    • 生态相对较新,不如Webpack成熟。
    • 某些高级功能可能需要自定义插件支持。

5. Webpack执行流程

  1. 初始化
    • 读取配置文件,合并配置项,初始化Compiler对象。
  2. 编译
    • 从入口点出发,递归解析模块依赖,调用相应的loader进行代码转换。
  3. 构建模块
    • 将每个模块封装成一个module对象,处理各种文件类型。
  4. 生成Chunk
    • 根据entry配置,将不同模块组合成一个或多个Chunk。
  5. 输出
    • 将Chunk转换成文件,输出到指定的目录。

6. Loader的使用和作用

使用:

  • 在Webpack配置文件的module.rules中定义loader。
module: {
  rules: [
    {
      test: /\.css$/,
      use: ['style-loader', 'css-loader']
    }
  ]
}

作用:

  • 将各种文件类型转换为Webpack可以处理的模块。
  • 例如,将Sass转换为CSS,将ES6+代码转换为ES5。

7. Webpack配置经验

  • 基本配置(入口、输出)。
  • 加载器配置(例如Babel、CSS)。
  • 插件配置(例如HtmlWebpackPlugin、CleanWebpackPlugin)。
  • 优化配置(代码分割、缓存)。

8. 引用数据类型和复杂数据类型的区别

  • 引用数据类型:
    • 数据是存储在堆内存中的,变量保存的是对内存地址的引用。
  • 复杂数据类型:
    • 一般指引用数据类型,相对于简单数据类型(如Number、String等)而言。

9. 引用数据类型的几个例子

  • 对象(Object)
  • 数组(Array)
  • 函数(Function)
  • 日期(Date)
  • 正则表达式(RegExp)

10. Null是不是对象,为什么?

Null本质上不是对象

  • Null是一个表示“空”的原始值,不是对象类型。
  • 由于历史原因,typeof null 返回 “object”,这是JavaScript的一个bug。
  • 在JavaScript的早期实现中,null在底层表示为一个全零的对象引用(类似于C++),因此typeof null 返回 “object”。

11. const, let, var定义变量的底层实现

  • var:
    • 变量声明会被提升到函数或全局作用域顶部,初始化为undefined
    • 存在函数作用域,没有块级作用域。
  • let:
    • 变量声明存在暂时性死区(TDZ),在声明之前无法访问。
    • 存在块级作用域。
  • const:
    • let相同,但声明时必须初始化,且不能重新赋值。

12. 数据类型的判断方法

  • typeof:判断基本类型和函数。
  • instanceof:判断对象是否是某个构造函数的实例。
  • Object.prototype.toString.call:判断对象的具体类型。
  • Array.isArray:判断是否为数组。

13. 如何判断对象时排除null

使用严格相等(===)来排除null

if (obj !== null && typeof obj === 'object') {
  // obj是一个对象,但不是null
}

14. 原型和原型链

  • 原型(Prototype):
    • 每个JavaScript对象(除了null)都与另一个对象相关联,这个对象称为原型。
    • 对象可以通过原型继承属性和方法。
  • 原型链(Prototype Chain):
    • 是对象间通过__proto__链接起来的链条,用于实现继承。
    • 当访问一个对象的属性时,JavaScript会先查找对象自身的属性,如果找不到则沿着原型链向上查找。

15. Null和Undefined的区别

  • Null:
    • 明确赋值表示“没有值”。
    • typeof null返回"object"。
  • Undefined:
    • 变量声明未赋值时的默认值。
    • typeof undefined返回"undefined"。

16. Promise的方法

  • then()
    • 用于处理Promise成功的结果,接收两个回调函数:第一个是成功时执行,第二个是失败时执行。
  • catch()
    • 用于处理Promise的失败结果,相当于then(null, rejection)

人生建议

深入理解JavaScript的底层原理和高级用法,对你理解和开发基于JavaScript的框架(如Vue)的源码有很大帮助。掌握Promise的实现原理也会提高你的编程能力和对异步编程的理解。通过不断学习和实践,你也有机会开发并开源自己的框架。

算法

快排

void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;

    int i = l - 1, j = r + 1, x = q[l + r >> 1];
    while (i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    quick_sort(q, l, j), quick_sort(q, j + 1, r);
}

二分

  • 左二分
 int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}
  • 右二分
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;   
        else l = mid + 1;
    }
    return l;
}

LRU

class LRUCache {
    constructor(capacity) {
        this.capacity = capacity;
        this.cache = new Map();
    }

    get(key) {
        if (!this.cache.has(key)) {
            return -1;
        }
        const value = this.cache.get(key);
        this.cache.delete(key);
        this.cache.set(key, value);
        return value;
    }

    put(key, value) {
        if (this.cache.has(key)) {
            this.cache.delete(key);
        }
        this.cache.set(key, value);
        if (this.cache.size > this.capacity) {
            this.cache.delete(this.cache.keys().next().value);// delete the first key
        }
    }
}


// 测试 LRUCache
const lruCache = new LRUCache(3);
lruCache.put(1, 1);
lruCache.put(2, 2);
lruCache.put(3, 3);
console.log(lruCache.get(1)); // 1
lruCache.put(4, 4);
console.log(lruCache.get(2)); // -1 (因为 key 2 已被淘汰)
lruCache.put(5, 5);
console.log(lruCache.get(3)); // -1 (因为 key 3 已被淘汰)
console.log(lruCache.get(4)); // 4
console.log(lruCache.get(5)); // 5

Linus

布局

面试

操作系统

缓存淘汰策略

  • 先进先出策略(FIFO,First In,First Out)

  • 最少使用策略(LFU,Least Rrequently Used)

  • 最近最少使用策略(LRU,Least Recently Used)

2023年前端面试题汇总包括以下问题: 1. 请解释下什么是响应式设计(Responsive Design)? 响应式设计是一种设计和开发网站的方法,使其能够在不同设备上提供最佳的用户体验。通过使用媒体查询、弹性网格布局以及其他技术手段,网站可以根据设备的屏幕大小和特性自适应地调整布局和样式。 2. 谈谈你理解的盒模型(Box Model)? 盒模型是指在网页布局中,每个元素都被看作是一个矩形的盒子。它由内容区域(content)、内边距(padding)、边框(border)和外边距(margin)组成。这些部分共同决定了元素在页面中的尺寸、位置以及与其他元素之间的间距。 3. 解释一下什么是跨域(Cross-Origin Resource Sharing,CORS)? 跨域指的是在浏览器发送请求时,当前页面所在的域与该请求要访问的资源所在的域不一致。出于安全原因,浏览器会限制跨域请求。CORS 是一种机制,允许服务器在响应中设置一些头部信息,告诉浏览器该服务器允许哪些跨域请求。 4. 如何优化网页的加载性能? 有多种方法可以优化网页的加载性能,以下是一些常见的技术: - 使用浏览器缓存,减少对服务器的请求次数。 - 压缩和合并 CSS 和 JavaScript 文件,减小文件大小。 - 使用懒加载和延迟加载来延迟加载非关键资源。 - 优化图片,使用适当的格式和压缩算法。 - 使用 CDN(内容分发网络)来加速资源的加载。 - 减少 HTTP 请求次数,合并和内联文件。 - 优化服务器响应时间,减少网络延迟。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码有点萌

谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值