前端面试题汇总

前端

Git

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会一直向前

git log

Redis

MySQL

React

HOOK

Vue

blog

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

高频面试题

原生

  • 盒模型

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

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

  • BFC(块格式上下文)

    • margin折叠问题

      • 代码

        BFC Example

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 (


        You clicked {count} times


        <button onClick={() => setCount(count + 1)}>Click me

        );
        }

    • 类组件

      • 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 (


        You clicked {this.state.count} times


        Click me

        );
        }
        }

  • setState

  • 受控组件和非受控组件

    • 受控组件

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

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

    • useMemo

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

      • 返回的是一个值

      • 接受依赖项数组

    • useCallback

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

      • 返回的是一个函数

      • 接受依赖项数组

  • 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 (


        Count: {this.state.count}


        Increment

        );
        }
        }

        // 使用有状态组件
        const App = () => {
        return ;
        };

        export default App;

      • 理解

        • 是类组件

        • 有继承

        • 可以使用this

        • 可以使用react生命周期

        • 使用较多,容易频繁出发生命周期钩子函数,影响性能

        • 内部使用state,维护自身状态的变化,有状态组件根据外部组件传入的props和自身的state进行渲染。

    • 无状态组件

      • 它们只依赖于传入的 props 来渲染内容

        // 无状态函数组件
        const Greeting = ({ name }) => {
        return

        Hello, {name}!

        ;
        };

        // 使用无状态组件
        const App = () => {
        return ;
        };

        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 (


        Count: {count}


        Increment

        );
        };

        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);
        }
        
    2. static getDerivedStateFromProps(nextProps, prevState)

      • 作用:根据传入的props更新state。此方法必须是静态方法,不能访问this。
      • 使用场景:当state需要根据props变化时使用。
      • 示例
        static getDerivedStateFromProps(nextProps, prevState) {
          if (nextProps.value !== prevState.value) {
            return {
              value: nextProps.value
            };
          }
          return null;
        }
        
    3. render()

      • 作用:返回要渲染的React元素。此方法是纯函数,不应包含任何副作用。
      • 使用场景:定义组件的UI。
      • 示例
        render() {
          return <div>{this.state.value}</div>;
        }
        
    4. 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;
        }
        
    3. render()

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

      • 作用:在DOM更新前捕获一些信息。返回的值会作为componentDidUpdate的第三个参数。
      • 使用场景:在DOM变更前获取一些信息(例如滚动位置)。
      • 示例
        getSnapshotBeforeUpdate(prevProps, prevState) {
          if (prevProps.list.length < this.props.list.length) {
            return this.listRef.scrollHeight;
          }
          return null;
        }
        
    5. 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

    • 场景

      • this.$nextTick

        • 确保你在数据变化引起的 DOM 更新完成后执行某些操作。
      • @blur 事件

        • 绑定在输入框上,当输入框失去焦点时触发 handleBlur 方法。

Other

  • 防抖

    • // 防抖 指定时间内只能触发一次
      function debounce(fn, delay) {
      let timer = null;
      return function (…args) { // 使用剩余参数语法来接收 debounce 函数的参数,并将其传递给原始函数 fn
      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 来触发防抖处理

  • 节流

    • // 节流 指定时间内最多触发一次
      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(); // window

// 什么时候使用箭头函数 什么时候使用普通函数
// 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属性,以此来实现图片的延迟加载。

      • (1) window.innerHeight 是浏览器可视区的高度

      • (2)document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的过的距离

      • (3)imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离)

      • (4)图片加载条件: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:服务器作为网关或代理,未能及时从上游服务器收到响应。

面经

1

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配置经验

是的,我写过一些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)

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

代码有点萌

谢谢

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

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

打赏作者

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

抵扣说明:

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

余额充值