裸辞8年前端的面试笔记——JavaScript篇(一)

news2025/5/9 17:56:55

裸辞后的第二个月开始准备找工作,今天是第三天目前还没有面试,现在的行情是一言难尽,都在疯狂的压价。

下边是今天复习的个人笔记

一、事件循环

JavaScript 的事件循环(Event Loop)是其实现异步编程的关键机制。

从原理上讲,JavaScript 是单线程语言,只有一个主线程来执行代码,这意味着同一时间只能做一件事。但为了实现异步操作(比如处理用户交互、网络请求等),引入了事件循环机制。
事件循环涉及到几个重要概念:

  1. 调用栈(Call Stack): 是一种数据结构,用于记录函数的调用关系。函数调用时入栈,执行完毕后出栈。
  2. 任务队列(Task Queue): 也叫消息队列,用于存放异步操作的回调函数。当异步操作完成时,对应的回调函数会被放入任务队列。
  3. 宏任务(Macrotask): 包括 script (整体代码)、setTimeout、setInterval、setImmediate(Node.js 环境)、requestAnimationFrame 等。
  4. 微任务(Microtask): 包括 Promise 的 then、catch、finally,MutationObserver 等。

事件循环的执行过程大致如下:

  1. 首先执行调用栈中的同步任务。
  2. 当遇到异步任务时,异步任务会被挂起,不会阻塞主线程,继续执行同步任务。
  3. 当同步任务执行完毕后,开始处理微任务队列,依次执行微任务队列中的任务。
  4. 微任务执行完毕后,开始执行宏任务队列中的任务,每执行一个宏任务,就会检查并执行微任务队列。
  5. 重复上述过程,不断循环,这就是事件循环。

例如:

console.log('start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise then');
});

console.log('end');

在这段代码中,首先 console.log('start') console.log('end') 作为同步任务在调用栈中依次执行。setTimeout 是宏任务,会被放到宏任务队列。Promise.resolve().then() 是微任务,会被放到微任务队列。当同步任务执行完后,开始执行微任务队列中的 Promisethen 回调,打印 Promise then,最后执行宏任务队列中的 setTimeout 回调,打印 setTimeout

二、Promise.all 和 Promise.race

Promise.all 和 Promise.race ,它们都是 Promise 的静态方法,在处理多个 Promise 时非常有用,以下是它们的详细介绍:

Promise.all

  • 它接受一个包含多个 Promise 对象的可迭代对象(比如数组)作为参数。
  • 只有当传入的所有 Promise 都成功时,Promise.all 才会返回一个成功的 Promise,其结果是一个包含所有 Promise 结果的数组,顺序和传入的 Promise 顺序一致。
  • 只要有一个 Promise 失败,Promise.all 就会立即返回一个失败的 Promise,失败原因就是第一个失败的 Promise 的原因。

例如:

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then((values) => {
      console.log(values); //输出: [1, 2, 3]
    })
  .catch((error) => {
      console.error(error);
    });

有失败的Promise:

const promise1 = Promise.resolve(1);
const promise2 = Promise.reject(new Error('Promise 2 failed'));
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
  .then((values) => {
      console.log(values); // 不会输出
    })
  .catch((error) => {
      console.error(error.message); // 输出: Promise 2 failed
    });

Promise.race

  • 同样接受一个包含多个 Promise 对象的可迭代对象作为参数。
  • 只要其中一个 Promise 率先改变状态(无论是成功还是失败),Promise.race 就会返回这个 Promise 的结果或原因。

例如:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise 1 resolved');
  }, 2000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('Promise 2 failed'));
  }, 1000);
});

Promise.race([promise1, promise2])
  .then((value) => {
      console.log(value);
    })
  .catch((error) => {
      console.error(error.message); // Promise 2 failed
    });

简单来说,Promise.all 强调所有 Promise 都成功,Promise.race 则关注谁先改变状态。

纯JS实现Promise,并集成all和race

下面是一个简单实现 Promise 并添加 all 和 race 方法的代码示例,解释了其基本原理和实现思路:

// 自定义Promise类
function MyPromise(executor) {
    this.status = 'pending';
    this.value = null;
    this.reason = null;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (val) => {
        if (this.status === 'pending') {
            this.status = 'fulfilled';
            this.value = val;
            this.onResolvedCallbacks.forEach(callback => callback(this.value));
        }
    };

    const reject = (err) => {
        if (this.status === 'pending') {
            this.status ='rejected';
            this.reason = err;
            this.onRejectedCallbacks.forEach(callback => callback(this.reason));
        }
    };

    try {
        executor(resolve, reject);
    } catch (error) {
        reject(error);
    }
}

// Promise.prototype.then方法实现
MyPromise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function'? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function'? onRejected : reason => { throw reason };

    let nextPromise;
    if (this.status === 'fulfilled') {
        nextPromise = new MyPromise((resolve, reject) => {
            try {
                const x = onFulfilled(this.value);
                resolvePromise(nextPromise, x, resolve, reject);
            } catch (error) {
                reject(error);
            }
        });
    }

    if (this.status ==='rejected') {
        nextPromise = new MyPromise((resolve, reject) => {
            try {
                const x = onRejected(this.reason);
                resolvePromise(nextPromise, x, resolve, reject);
            } catch (error) {
                reject(error);
            }
        });
    }

    if (this.status === 'pending') {
        nextPromise = new MyPromise((resolve, reject) => {
            this.onResolvedCallbacks.push((value) => {
                try {
                    const x = onFulfilled(value);
                    resolvePromise(nextPromise, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });

            this.onRejectedCallbacks.push((reason) => {
                try {
                    const x = onRejected(reason);
                    resolvePromise(nextPromise, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });
        });
    }

    return nextPromise;
};

// 辅助函数,处理then方法中返回值的逻辑
function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise'));
    }

    if (x instanceof MyPromise) {
        x.then(resolve, reject);
    } else if (typeof x === 'object' || typeof x === 'function') {
        if (x === null) {
            return resolve(x);
        }

        let called = false;
        try {
            const then = x.then;
            if (typeof then === 'function') {
                then.call(
                    x,
                    (y) => {
                        if (called) return;
                        called = true;
                        resolvePromise(promise2, y, resolve, reject);
                    },
                    (r) => {
                        if (called) return;
                        called = true;
                        reject(r);
                    }
                );
            } else {
                resolve(x);
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(error);
        }
    } else {
        resolve(x);
    }
}

// 实现Promise.all方法
MyPromise.all = function (promises) {
    return new MyPromise((resolve, reject) => {
        const result = [];
        let count = 0;
        if (promises.length === 0) {
            resolve(result);
        } else {
            promises.forEach((p, index) => {
                MyPromise.resolve(p)
                   .then((value) => {
                        result[index] = value;
                        count++;
                        if (count === promises.length) {
                            resolve(result);
                        }
                    })
                   .catch((error) => {
                        reject(error);
                    });
            });
        }
    });
};

// 实现Promise.race方法
MyPromise.race = function (promises) {
    return new MyPromise((resolve, reject) => {
        promises.forEach((p) => {
            MyPromise.resolve(p)
               .then((value) => {
                    resolve(value);
                })
               .catch((error) => {
                    reject(error);
                });
        });
    });
};

使用自定义的 MyPromise

// 使用示例
const promise1 = new MyPromise((resolve) => {
    setTimeout(() => {
        resolve(1);
    }, 1000);
});

const promise2 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        reject(new Error('Promise 2 failed'));
    }, 500);
});

// 使用then方法
promise1
   .then((value) => {
        console.log(value);
    })
   .catch((error) => {
        console.error(error);
    });

// 使用all方法
MyPromise.all([promise1, promise2])
   .then((values) => {
        console.log(values);
    })
   .catch((error) => {
        console.error(error);
    });

// 使用race方法
MyPromise.race([promise1, promise2])
   .then((value) => {
        console.log(value);
    })
   .catch((error) => {
        console.error(error);
    });

在上述代码中,首先定义了一个 MyPromise 类,实现了基本的 Promise 功能,包括 then 方法。然后添加了 all 和 race 静态方法,分别用于按顺序处理多个 Promise(all)和谁先有结果就返回谁(race)。resolvePromise 函数则处理了 then 方法中返回值的复杂逻辑,确保遵循 Promise/A+ 规范。

三、闭包

闭包是面试中常见的一个考点,复习也是很有必要的,在工作中使用闭包的场景很多比如在Vue和React组件就是个大闭包,还有防抖节流等函数的封装等等。

1. 什么是闭包?

闭包是指函数和与其相关的词法环境的组合。当一个内部函数在其外部函数返回后仍然能访问外部函数的变量时,就创建了闭包。

function outer() {
    let count = 0;
    function inner() {
        count++;
        console.log(count);
    }
    return inner;
}

const closureFn = outer();
closureFn(); // 1
closureFn(); // 2

在这个例子中,inner函数形成了闭包,即使 outer 函数已经执行完毕,inner 函数依然可以访问 outer 函数作用域内的 count 变量。

2. 闭包有什么作用?

  • 数据私有性: 可以隐藏变量,通过闭包,外部代码无法直接访问函数内部的变量,只能通过闭包返回的函数来操作这些变量,实现数据的封装。
  • 状态保存: 闭包能记住创建时的状态,比如在计数器的例子中,每次调用闭包函数,都能记住上次 count 的值并进行操作。
  • 柯里化: 闭包是实现函数柯里化的基础,柯里化可以将多参数函数转化为一系列单参数函数,提高函数的复用性和灵活性。柯里化后边会延伸描述

3. 闭包可能会带来什么问题?

  • 内存泄漏: 如果闭包函数一直存在,并且引用了一些大的对象或不再需要的变量,这些变量不会被垃圾回收机制回收,可能会导致内存占用过高,出现内存泄漏。例如:
    function createBigObject() {
        const bigArray = new Array(1000000).fill(0);
        return function () {
            // 闭包函数一直存在,bigArray无法被回收
            console.log('closure');
        };
    }
    
    const leakyClosure = createBigObject();
    
  • 变量的值不是预期的: 在循环中使用闭包时,如果不注意,可能会得到意外的结果。比如:
    const functions = [];
    for (var i = 0; i < 5; i++) {
        functions.push(() => {
            console.log(i);
        });
    }
    functions.forEach(fn => fn()); 
    // 输出 5 5 5 5 5,因为这里的i是最后循环结束时的值
    
    可以通过立即执行函数或使用 let 关键字来解决这个问题。如下:
    const functions = [];
    for (let i = 0; i < 5; i++) {
        functions.push(() => {
            console.log(i);
        });
    }
    functions.forEach(fn => fn()); 
    // 输出 0 1 2 3 4
    

4. 如何避免闭包导致的内存泄漏?

当闭包不再使用时,手动将闭包函数赋值为 null,这样相关的变量就可以被垃圾回收机制回收。例如:

function createClosure() {
    let data = { a: 1 };
    return function () {
        console.log(data.a);
    };
}

let closureFn = createClosure();
// 使用闭包函数
closureFn();
// 不再使用闭包时,将其赋值为null
closureFn = null;

5. 实际开发中,不专门手动将闭包引用的变量置为null

实际开发中,闭包导致的内存泄漏本质是 “外部引用未正确释放”,而非闭包语法本身的问题。现代 GC 机制和框架已能处理大部分场景,手动置空闭包变量既不现实也无必要。开发者的核心任务是:

  • 正确管理外部依赖(移除事件监听、清除定时器、避免不合理的全局引用);
  • 依赖框架的生命周期钩子处理副作用,让闭包随上下文自然释放。

只有在极端或不规范的场景下,才需针对性地手动清理,但这也应优先通过切断外部引用来实现,而非直接操作闭包内部的变量。

四、柯里化

**柯里化(Currying)**是一种在函数式编程中广泛使用的技术,它允许你将一个多参数函数转换为一系列单参数函数。以下从定义、原理、用途、示例等方面详细介绍柯里化。

定义与原理

  • 定义: 柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。简单来说,就是将一个多参数函数拆分成多个单参数函数。
  • 原理: 利用闭包的特性,让函数记住之前传入的参数,当参数数量达到原函数所需的参数数量时,执行原函数逻辑。

用途

参数复用: 当多次调用同一个函数,并且传递的参数大部分相同时,使用柯里化可以复用这些相同的参数。
延迟计算: 可以在需要的时候再传入剩余的参数进行计算,而不是一次性传入所有参数。
动态创建函数: 根据不同的参数动态生成不同的函数。

示例

以下是一个简单的 JavaScript 示例,展示如何实现柯里化:

// 定义一个普通的加法函数
function add(a, b) {
    return a + b;
}

// 实现柯里化函数
function curry(func) {
    return function curried(...args) {
        if (args.length >= func.length) {
            return func.apply(this, args);
        } else {
            return function (...nextArgs) {
                return curried.apply(this, args.concat(nextArgs));
            };
        }
    };
}

// 将 add 函数进行柯里化
const curriedAdd = curry(add);

// 使用柯里化函数
const add5 = curriedAdd(5);
console.log(add5(3)); // 输出 8    

代码解释

  • curry 函数: 接受一个函数 func 作为参数,返回一个新的函数 curried。
  • curried 函数: 接受任意数量的参数 args,如果 args 的长度大于或等于原函数 func 的参数长度,则直接调用 func 并返回结果;否则,返回一个新的函数,该函数会将之前的参数和新传入的参数合并后再次调用 curried 函数。
  • curriedAdd 函数: 是 add 函数柯里化后的结果。通过 curriedAdd(5) 得到一个新的函数 add5,这个函数记住了之前传入的参数 5,当调用 add5(3) 时,将 5 和 3 相加并返回结果。

实际应用场景

  • **日志记录:**在日志记录时,通常会有一些固定的参数(如日志级别、日志来源等),可以使用柯里化来复用这些参数。
    function log(level, source, message) {
        console.log(`[${level}] [${source}] ${message}`);
    }
    
    const curriedLog = curry(log);
    const errorLog = curriedLog('ERROR');
    const appErrorLog = errorLog('App');
    appErrorLog('Something went wrong!'); // 输出 [ERROR] [App] Something went wrong!
    
  • 事件处理: 在处理事件时,可能需要传递一些额外的参数,可以使用柯里化来动态创建事件处理函数。
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
    </head>
    
    <body>
        <button id="myButton">Click me</button>
        <script>
            function handleClick(message, event) {
                console.log(`${message}: ${event.type}`);
            }
    
            const curriedHandleClick = curry(handleClick);
            const clickWithMessage = curriedHandleClick('Button clicked');
    
            const button = document.getElementById('myButton');
            button.addEventListener('click', clickWithMessage);
        </script>
    </body>
    
    </html>
    
    在这个示例中,通过柯里化创建了一个带有固定消息的事件处理函数 clickWithMessage,当按钮被点击时,会输出相应的日志信息。

不推荐使用柯里化的场景

  • 简单一次性调用: 若函数仅调用一次,且参数无复用可能,直接调用普通函数更高效(如 sum(1, 2, 3) 无需柯里化)。
  • 参数顺序依赖强或易混淆: 柯里化要求严格按参数顺序传参,若参数含义不明确(如log('ERROR', 'App', '消息')中 ‘App’ 可能是来源或消息),可能导致调用时参数错位。
  • 追求极致性能的场景: 虽然现代引擎优化较好,但柯里化涉及闭包和多层函数嵌套,在极端高频调用(如循环内)时可能存在微小性能损耗(需实测验证)

总结

  • 用: 当需要参数复用、延迟计算、函数组合或动态生成定制函数,且代码风格兼容函数式思维时。
  • 慎: 当参数逻辑复杂、可读性可能受损,或团队对 FP 不熟悉时,优先考虑更直观的参数传递方式(如对象、默认参数)。

柯里化的核心价值在于将 “不变的部分” 与 “变化的部分” 分离,通过函数的 “预配置” 提高代码的灵活性和复用性。实际开发中,可从小规模场景(如工具函数、配置类函数)开始尝试,逐步判断是否符合项目需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2371677.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux中安装mysql8,转载及注意事项

一、先前往官网下载mysql8 下载地址&#xff1a; https://dev.mysql.com/downloads/选择Linux 二、删除Linux中的mysql&#xff08;如果有的话&#xff09;&#xff0c;上传安装包 1、先查看mysql是否存在&#xff0c;命令如下&#xff1a; rpm -qa|grep -i mysql如果使用这…

SpringBoot的汽车商城后台管理系统源码开发实现

概述 汽车商城后台管理系统专为汽车4S店和经销商设计&#xff0c;提供全面的汽车管理系统解决方案。 主要内容 1. 核心功能模块 系统提供以下主要功能&#xff1a; ​​销售管理​​&#xff1a;记录销售信息&#xff0c;跟踪交易进度​​客户管理​​&#xff1a;维护客户…

DeepSeek实战--手搓实现Agent

1.背景 要学习AI agent&#xff0c;只会用agent 框架&#xff0c;还不够&#xff0c;一旦框架出现问题&#xff0c;没法快速的排查出问题。 学习就应该“知其然&#xff0c;更应该知其所以然” &#xff0c;今天我们就用编码的方式实现一个简单的agent 。我们模拟一套AI学生评…

线性代数——行列式⭐

目录 一、行列式的定义⭐ 1-1、三阶行列式练习 1-2、下面介绍下三角行列式、上三角行列式、对角行列式 ​编辑 二、行列式的性质 2-1、性质1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6 ​编辑 2-2、性质7 2- 3、拉普拉斯定理、克莱姆法则 三…

iPhone手机连接WiFi异常解决方法

iPhone手机连接WiFi异常解决方法 一、问题现象二、iPhone连不上可能的原因三、基础排查与快速修复第一步:重启大法第二步:忽略网络,重新认证第三步:关闭“私有无线局域网地址”第四步:修改DNS服务器第五步:还原网络设置四、路由器端排查及设置关闭MAC地址过滤或添加到白名…

学习设计模式《八》——原型模式

一、基础概念 原型模式的本质是【克隆生成对象】&#xff1b; 原型模式的定义&#xff1a;用原型实例指定创建对象的种类&#xff0c;并通过拷贝这些原型创建新的对象 。 原型模式的功能&#xff1a; 1、通过克隆来创建新的对象实例&#xff1b; 2、为克隆出来的新对象实例复制…

疗愈服务预约小程序源码介绍

基于ThinkPHP、FastAdmin和UniApp开发的疗愈服务预约小程序源码&#xff0c;这款小程序在功能设计和用户体验上都表现出色&#xff0c;为疗愈行业提供了一种全新的服务模式。 该小程序源码采用了ThinkPHP作为后端框架&#xff0c;保证了系统的稳定性和高效性。同时&#xff0c…

【随笔】Google学术:but your computer or network may be sending automated queries.

文章目录 一、问题复述二、问题原因三、解决 前提&#xff1a;你的xxx是自己做的&#xff0c;你自己可以管理&#xff0c;而不是用的那些劣质✈场。 一、问题复述 &#x1f7e2;如下图所示&#xff1a;可以打开谷歌学术&#xff0c;但是一搜索就是这个界面。 二、问题原因 …

长事务:数据库中的“隐形炸弹“——金仓数据库运维避坑指南

引言&#xff1a;凌晨三点的告警 "张工&#xff01;生产库又告警了&#xff01;"凌晨三点的电话铃声总是格外刺耳。运维团队发现数据库频繁进入单用户模式&#xff0c;排查发现某核心表的年龄值&#xff08;Age&#xff09;已突破20亿大关。经过一夜奋战&#xff0c…

ubuntu nobel + qt5.15.2 设置qss语法识别正确

问题展示 解决步骤 首选项里面的高亮怎么编辑选择都没用。如果已经有generic-highlighter和css.xml&#xff0c;直接修改css.xml文件最直接&#xff01; 在generic-highlighter目录下找到css.xml文件&#xff0c;位置是&#xff1a;/opt/Qt/Tools/QtCreator/share/qtcreator/…

Unity-Socket通信实例详解

今天我们来讲解socket通信。 首先我们需要知道什么是socket通信&#xff1a; Socket本质上就是一个个进程之间网络通信的基础&#xff0c;每一个Socket由IP端口组成&#xff0c;熟悉计网的同学应该知道IP主要是应用于IP协议而端口主要应用于TCP协议&#xff0c;这也证明了Sock…

MATLAB仿真定点数转浮点数(对比VIVADO定点转浮点)

MATLAB仿真定点数转浮点数 定点数可设置位宽&#xff0c;小数位宽&#xff1b;浮点数是单精度浮点数 对比VIVADO定点转浮点 目录 前言 一、定点数 二、浮点数 三、定点数转浮点数 四、函数代码 总结 前言 在FPGA上实现算法时&#xff0c;相比MATLAB实现往往需要更长的开发…

【计算机网络】Cookie、Session、Token之间有什么区别?

大家在日常使用浏览器时可能会遇到&#xff1a;是否清理Cookie&#xff1f;这个问题。 那么什么是Cookie呢&#xff1f;与此相关的还有Session、Token这些。这两个又是什么呢&#xff1f; 本文将对这三个进行讲解区分&#xff0c;如果对小伙伴有帮助的话&#xff0c;也请点赞、…

SpringCloud服务拆分:Nacos服务注册中心 + LoadBalancer服务负载均衡使用

SpringCloud中Nacos服务注册中心 LoadBalancer服务负载均衡使用 前言Nacos工作流程nacos安装docker安装window安装 运行nacos微服务集成nacos高级特性1.服务集群配置方法效果图模拟服务实例宕机 2.权重配置3.环境隔离 如何启动集群节点本地启动多个节点方法 LoadBalancer集成L…

Apache Doris 使用指南:从入门到生产实践

目录 一、Doris 核心概念 1.1 架构组成 1.2 数据模型 二、Doris 部署方式 2.1 单机部署&#xff08;测试环境&#xff09; 2.2 集群部署&#xff08;生产环境&#xff09; 三、数据操作指南 3.1 数据库与表管理 3.2 数据导入方式 3.2.1 批量导入 3.2.2 实时导入 3.…

26届秋招收割offer指南

26届暑期实习已经陆续启动&#xff0c;这也意味着对于26届的同学们来说&#xff0c;“找工作”已经提上了日程。为了帮助大家更好地准备暑期实习和秋招&#xff0c;本期主要从时间线、学习路线、核心知识点及投递几方面给大家介绍&#xff0c;希望能为大家提供一些实用的建议和…

拷贝多个Excel单元格区域为图片并粘贴到Word

Excel工作表Sheet1中有两个报表&#xff0c;相应单元格区域分别定义名称为Report1和Report2&#xff0c;如下图所示。 现在需要将图片拷贝图片粘贴到新建的Word文档中。 示例代码如下。 Sub Demo()Dim oWordApp As ObjectDim ws As Worksheet: Set ws ThisWorkbook.Sheets(&…

【Bluedroid】蓝牙 SDP(服务发现协议)模块代码解析与流程梳理

本文深入剖析Bluedroid蓝牙协议栈中 SDP&#xff08;服务发现协议&#xff09;服务记录的全生命周期管理流程&#xff0c;涵盖初始化、记录创建、服务搜索、记录删除等核心环节。通过解析代码逻辑与数据结构&#xff0c;揭示各模块间的协作机制&#xff0c;包括线程安全设计、回…

中国自动驾驶研发解决方案,第一!

4月28日&#xff0c;IDC《中国汽车云市场(2024下半年)跟踪》报告发布&#xff0c;2024下半年中国汽车云市场整体规模达到65.1亿元人民币&#xff0c;同比增长27.4%。IDC认为&#xff0c;自动驾驶技术深化与生成式AI的发展将为汽车云打开新的成长天花板&#xff0c;推动云计算在…

Kubernetes(k8s)学习笔记(四)--入门基本操作

本文通过kubernetes部署tomcat集群&#xff0c;来学习和掌握kubernetes的一些入门基本操作 前提条件 1.各个节点处于Ready状态&#xff1b; 2.配置好docker镜像库(否则会出现ImagePullBackOff等一些问题)&#xff1b; 3.网络配置正常(否则即使应用发布没问题&#xff0c;浏…