前端取经路——JavaScript修炼:悟空的九大心法
大家好,我是老十三,一名前端开发工程师。JavaScript如同孙悟空的七十二变,变化多端却又充满威力。本篇文章我将带你攻克JS中最令人头疼的九大难题,从闭包陷阱到原型链继承,从异步编程到性能优化。每个难题都配有实战代码,手把手教你化解这些JS"妖怪"。无论你是否已入门,这些心法都能帮你在前端修行路上少走弯路,早日修成正果。
修得CSS真身后,是时候踏入JavaScript的修炼场,领悟悟空的九大心法。这些心法看似简单,实则玄妙,掌握它们,你将拥有应对前端各种妖魔鬼怪的金刚不坏之躯。
🐒 第一难:原型链继承 - 猴王的传承之道
问题:为什么JavaScript中对象能调用不属于自身的方法?这种"从无到有"的魔法是如何实现的?
深度技术:
JavaScript的原型链继承是其最具特色的设计,不同于传统的类继承,它通过原型对象实现属性和方法的传递。理解原型链,关键在于掌握__proto__
、prototype
和constructor
三者的关系。
原型链的精髓在于"委托"而非"复制",这种设计思想既节省内存,又提供了极大的灵活性,但也带来了this
指向等难题。ES6的类语法虽然使继承更易用,但底层仍是基于原型链实现。
代码示例:
// 传统原型继承方式
function Animal(name) {this.name = name;
}Animal.prototype.speak = function() {return `${this.name} makes a noise.`;
};function Monkey(name, trick) {// 调用父构造函数Animal.call(this, name);this.trick = trick;
}// 建立原型链
Monkey.prototype = Object.create(Animal.prototype);
// 修复构造函数指向
Monkey.prototype.constructor = Monkey;// 添加猴子特有方法
Monkey.prototype.doTrick = function() {return `${this.name} performs ${this.trick}!`;
};// 覆盖父类方法
Monkey.prototype.speak = function() {return `${this.name} says: I know ${this.trick}!`;
};// ES6类语法实现同样的继承
class ModernAnimal {constructor(name) {this.name = name;}speak() {return `${this.name} makes a noise.`;}
}class ModernMonkey extends ModernAnimal {constructor(name, trick) {super(name);this.trick = trick;}doTrick() {return `${this.name} performs ${this.trick}!`;}speak() {return `${this.name} says: I know ${this.trick}!`;}
}// 使用示例
const wukong = new Monkey('Sun Wukong', '72 transformations');
console.log(wukong.speak()); // "Sun Wukong says: I know 72 transformations!"
console.log(wukong.doTrick()); // "Sun Wukong performs 72 transformations!"
🔒 第二难:闭包陷阱 - 封印"妖气"的密室
问题:函数为什么能"记住"它的创建环境?闭包是强大法宝还是内存泄漏的源头?
深度技术:
闭包是JavaScript中最强大也最容易被误用的特性,它允许函数访问并保留其词法作用域,即使函数在其他作用域中执行。理解闭包,需要掌握词法作用域、执行上下文和垃圾回收机制。
闭包的应用极为广泛,从模块化模式、函数柯里化到React的状态管理,处处可见其身影。但不当使用会导致内存泄漏,特别是在事件处理和定时器中更需警惕。
代码示例:
// 基础闭包示例
function createCounter() {// 私有变量,外部无法直接访问let count = 0;// 返回闭包函数return {increment() {return ++count;},decrement() {return --count;},getValue() {return count;}};
}const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getValue()); // 2// 闭包陷阱:意外的内存泄漏
function setupHandler(element) {// 这里有一个大数组const hugeData = new Array(10000).fill('🐒');// 错误写法:事件处理器会持有hugeData的引用element.addEventListener('click', function() {console.log(hugeData.length); // hugeData被引用,无法释放});// 正确写法:只保留需要的数据const dataLength = hugeData.length;element.addEventListener('click', function() {console.log(dataLength); // 只保留了length值,hugeData可以被释放});
}// 闭包应用:函数柯里化(部分应用)
function multiply(a, b) {return a * b;
}function curry(fn) {return function(a) {return function(b) {return fn(a, b);};};
}const curriedMultiply = curry(multiply);
const double = curriedMultiply(2); // 闭包记住了a=2
console.log(double(5)); // 10
⏳ 第三难:异步编程 - Promise从入门到"大乘"
问题:如何驯服JavaScript的异步"猴性"?从回调地狱到async/await的进化之路有何玄机?
深度技术:
JavaScript的异步编程是前端修行的核心难关。从最初的回调函数,到Promise对象,再到async/await语法糖,异步处理范式不断进化。
理解异步的关键在于Event Loop(事件循环)机制,它决定了JavaScript引擎如何调度任务。掌握Promise的链式调用、错误处理和并发控制,是跨越"异步之坑"的必要法门。
代码示例:
// 回调地狱 - "五指山"困境
fetchUserData(userId, function(userData) {fetchUserPosts(userData.id, function(posts) {fetchPostComments(posts[0].id, function(comments) {fetchCommentAuthor(comments[0].authorId, function(author) {console.log(author.name);// 层层嵌套,难以维护}, handleError);}, handleError);}, handleError);
}, handleError);// Promise - "金箍棒"出世
fetchUserData(userId).then(userData => fetchUserPosts(userData.id)).then(posts => fetchPostComments(posts[0].id)).then(comments => fetchCommentAuthor(comments[0].authorId)).then(author => console.log(author.name)).catch(error => handleError(error));// Async/Await - "筋斗云"境界
async function getUserAuthor(userId) {try {const userData = await fetchUserData(userId);const posts = await fetchUserPosts(userData.id);const comments = await fetchPostComments(posts[0].id);const author = await fetchCommentAuthor(comments[0].authorId);return author.name;} catch (error) {handleError(error);}
}// Promise并发控制 - "一气化三清"
async function fetchAllUsersData(userIds) {// 并行请求所有用户数据const promises = userIds.map(id => fetchUserData(id));// 等待所有请求完成const usersData = await Promise.all(promises);return usersData;
}// Promise竞争 - "火眼金睛"选取最快
async function fetchFromFastestSource(resourceId) {try {const result = await Promise.race([fetchFromAPI1(resourceId),fetchFromAPI2(resourceId),fetchFromCache(resourceId)]);return result;} catch (error) {// 即使最快的失败了,其他请求仍在进行console.error('Fastest source failed:', error);// 可以继续等待其他结果}
}// Promise取消 - "定身法"
function fetchWithTimeout(url, ms) {const controller = new AbortController();const { signal } = controller;// 设置超时定时器const timeout = setTimeout(() => controller.abort(), ms);return fetch(url, { signal }).then(response => {clearTimeout(timeout);return response;}).catch(error => {if (error.name === 'AbortError') {throw new Error('Request timed out');}throw error;});
}
🧘 第四难:this指向 - JavaScript的"紧箍咒"
问题:为什么有时this指向window,有时又指向调用者?如何摆脱this带来的"头痛"?
深度技术:
this
是JavaScript中最令人困惑的概念之一,它不是编译时绑定,而是运行时绑定,取决于函数的调用方式。理解this
的关键是掌握四种绑定规则:默认绑定、隐式绑定、显式绑定和new绑定。
箭头函数与传统函数对this
的处理方式不同,它没有自己的this
,而是继承外围作用域的this
值。这种特性使箭头函数特别适合回调函数和事件处理器。
代码示例:
// 默认绑定:非严格模式下指向全局对象,严格模式下是undefined
function showThis() {console.log(this);
}
showThis(); // window(浏览器中)// 隐式绑定:this指向调用该方法的对象
const monkey = {name: 'Wukong',showName() {console.log(this.name);}
};
monkey.showName(); // "Wukong"// 隐式绑定丢失的情况
const showName = monkey.showName;
showName(); // undefined,this指向了全局对象// 显式绑定:使用call、apply和bind
function introduce(description) {console.log(`${this.name} is ${description}`);
}introduce.call(monkey, 'the Monkey King'); // "Wukong is the Monkey King"
introduce.apply(monkey, ['the Monkey King']); // "Wukong is the Monkey King"const introduceMonkey = introduce.bind(monkey);
introduceMonkey('a powerful warrior'); // "Wukong is a powerful warrior"// new绑定:构造函数中的this指向新创建的对象
function Disciple(name) {this.name = name;this.introduce = function() {console.log(`I am ${this.name}`);};
}const wukong = new Disciple('Sun Wukong');
wukong.introduce(); // "I am Sun Wukong"// 箭头函数:this继承自外围作用域
const tang = {name: 'Tang Monk',disciples: ['Sun Wukong', 'Zhu Bajie', 'Sha Wujing'],// 传统函数的this问题showDisciplesTraditional: function() {this.disciples.forEach(function(disciple) {console.log(`${this.name}'s disciple: ${disciple}`); // this.name是undefined});},// 使用箭头函数解决showDisciplesArrow: function() {this.disciples.forEach(disciple => {console.log(`${this.name}'s disciple: ${disciple}`); // 正确输出});}
};tang.showDisciplesTraditional(); // "undefined's disciple: Sun Wukong" 等
tang.showDisciplesArrow(); // "Tang Monk's disciple: Sun Wukong" 等
🔄 第五难:事件循环 - 宏任务与微任务的修行循环
问题:JavaScript如何在单线程环境下处理并发任务?为什么Promise比setTimeout先执行?
深度技术:
事件循环(Event Loop)是JavaScript运行时环境的核心机制,它解释了异步操作的执行顺序。理解事件循环,需要掌握调用栈、任务队列、微任务队列和渲染过程的交互方式。
事件循环的执行顺序遵循:同步代码 → 微任务(Promise, MutationObserver) → 宏任务(setTimeout, setInterval, I/O)的模式。这种机制保证了JavaScript的非阻塞特性,但也带来了定时器不精确等问题。
代码示例:
console.log('1. Script start'); // 同步代码setTimeout(() => {console.log('2. setTimeout callback'); // 宏任务
}, 0);Promise.resolve().then(() => {console.log('3. Promise.then 1'); // 微任务// 在微任务中添加的新微任务Promise.resolve().then(() => {console.log('4. Promise.then nested');});}).then(() => {console.log('5. Promise.then 2'); // 微任务链});console.log('6. Script end'); // 同步代码// 输出顺序:
// 1. Script start
// 6. Script end
// 3. Promise.then 1
// 4. Promise.then nested
// 5. Promise.then 2
// 2. setTimeout callback// 宏任务与微任务交互
async function demo() {console.log('A. Start');// 创建宏任务setTimeout(() => {console.log('B. setTimeout 1');// 宏任务中的Promise(微任务)Promise.resolve().then(() => {console.log('C. Promise in setTimeout');});// 宏任务中的宏任务setTimeout(() => {console.log('D. Nested setTimeout');}, 0);}, 0);// 创建微任务await Promise.resolve();console.log('E. After await');// 微任务之后的同步代码console.log('F. End');
}demo();// 输出顺序:
// A. Start
// E. After await
// F. End
// B. setTimeout 1
// C. Promise in setTimeout
// D. Nested setTimeout// 结合动画帧的高级例子
function animationWorkflow() {console.log('1. Start animation');// 安排在下一帧前执行requestAnimationFrame(() => {console.log('2. Animation frame');// 执行昂贵的DOM操作document.body.style.backgroundColor = 'red';// 微任务:在当前帧的DOM改变后但渲染前执行Promise.resolve().then(() => {console.log('3. Promise after RAF');document.body.style.backgroundColor = 'blue';});});// 安排在渲染后执行setTimeout(() => {console.log('4. Post-render operations');}, 0);
}
🧙♂️ 第六难:函数式编程 - 纯函数的"七十二变"
问题:为什么现代JavaScript越来越喜欢函数式编程?如何用纯函数改造代码,获得更好的可测试性和可维护性?
深度技术:
函数式编程是一种编程范式,它将计算过程视为数学函数的求值,避免状态变化和可变数据。JavaScript虽不是纯函数式语言,但支持高阶函数、闭包等函数式特性。
函数式编程的核心原则包括:纯函数、不可变数据、函数组合和避免副作用。掌握这些原则,能编写出更易于测试、调试和并行化的代码。
代码示例:
// 命令式编程:充满副作用
let disciples = ['Sun Wukong', 'Zhu Bajie', 'Sha Wujing'];
let powerLevels = [100, 80, 70];function increasePower(name, amount) {const index = disciples.indexOf(name);if (index !== -1) {powerLevels[index] += amount; // 修改外部状态}
}increasePower('Sun Wukong', 20);
console.log(powerLevels); // [120, 80, 70]// 函数式编程:纯函数与不可变数据
const discipleData = [{ name: 'Sun Wukong', power: 100 },{ name: 'Zhu Bajie', power: 80 },{ name: 'Sha Wujing', power: 70 }
];// 纯函数:无副作用,相同输入始终产生相同输出
function increasePowerPure(disciples, name, amount) {return disciples.map(disciple => disciple.name === name ? { ...disciple, power: disciple.power + amount }: disciple);
}const newDiscipleData = increasePowerPure(discipleData, 'Sun Wukong', 20);
console.log(newDiscipleData[0].power); // 120
console.log(discipleData[0].power); // 仍然是100,原数据未变// 函数组合:构建复杂逻辑
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);const addPower = (amount) => disciples => increasePowerPure(disciples, 'Sun Wukong', amount);const filterStrongDisciples = (minPower) => disciples => disciples.filter(d => d.power >= minPower);const getNames = disciples => disciples.map(d => d.name);// 组合多个操作
const getStrongDisciplesAfterTraining = pipe(addPower(20),filterStrongDisciples(90),getNames
);console.log(getStrongDisciplesAfterTraining(discipleData)); // ['Sun Wukong']// 柯里化:转换多参数函数为嵌套单参数函数
const curry = (fn) => {const arity = fn.length;return function curried(...args) {if (args.length >= arity) {return fn.apply(this, args);}return (...moreArgs) => curried.apply(this, [...args, ...moreArgs]);};
};const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
🍭 第七难:ES6+语法糖 - 现代JS的法宝大全
问题:ES6+引入的新特性如何帮助我们写出更简洁、更强大的代码?这些"语法糖"背后有哪些陷阱?
深度技术:
ES6及以后的JavaScript版本带来了大量语法糖和新特性,从解构赋值、扩展运算符到可选链和空值合并,这些特性极大地提升了开发效率和代码可读性。
然而,这些语法糖背后往往隐藏着复杂的实现机制,如果不了解其原理,可能导致代码性能和行为出现意外。掌握这些特性的内部工作方式,才能真正发挥其威力。
代码示例:
// 解构赋值:提取对象和数组中的值
const journey = {leader: 'Tang Monk',disciples: ['Sun Wukong', 'Zhu Bajie', 'Sha Wujing'],destination: 'Western Paradise',distance: 108000
};// 对象解构
const { leader: monk, disciples, destination } = journey;
console.log(monk); // 'Tang Monk'// 数组解构
const [firstDisciple, ...otherDisciples] = disciples;
console.log(firstDisciple); // 'Sun Wukong'
console.log(otherDisciples); // ['Zhu Bajie', 'Sha Wujing']// 默认值与重命名
const { distance: journeyLength = 0, difficulty = 'high' } = journey;
console.log(journeyLength); // 108000
console.log(difficulty); // 'high'(使用默认值)// 嵌套解构
const team = {leader: { name: 'Tang Monk', role: 'guide' },members: [{ name: 'Sun Wukong', power: 100 },{ name: 'Zhu Bajie', power: 80 }]
};const { leader: { name: leaderName }, members: [{ power: firstMemberPower }] } = team;
console.log(leaderName); // 'Tang Monk'
console.log(firstMemberPower); // 100// 扩展运算符:对象与数组的浅复制与合并
const baseCharacter = { health: 100, mana: 50 };
const wukong = { ...baseCharacter, name: 'Sun Wukong', power: 'Transformation' };// 注意:这是浅复制
const baseWithItems = { ...baseCharacter, items: ['staff', 'cloud'] };
baseWithItems.items.push('gold ring');
console.log(baseCharacter.items); // undefined,未受影响// 可选链与空值合并:安全地访问深度嵌套属性
const config = {user: {// preferences缺失}
};// 传统方式:需要多层检查
const theme = config.user && config.user.preferences && config.user.preferences.theme || 'default';// 可选链:简洁安全
const newTheme = config.user?.preferences?.theme ?? 'default';
console.log(newTheme); // 'default'// ?? 与 || 的区别
console.log(0 || 'fallback'); // 'fallback'(0被视为假值)
console.log(0 ?? 'fallback'); // 0(只有null和undefined才会触发后者)// 模板字面量:高级用法
const highlight = (strings, ...values) => {return strings.reduce((result, str, i) => {const value = values[i] || '';return `${result}${str}<span class="highlight">${value}</span>`;}, '');
};const name = 'Sun Wukong';
const power = 'Fiery Eyes';// 标签模板字面量
const result = highlight`The great ${name} has ${power}!`;
console.log(result);
// "The great <span class="highlight">Sun Wukong</span> has <span class="highlight">Fiery Eyes</span>!"
🛡️ 第八难:类型系统 - TypeScript的护体神功
问题:JavaScript的动态类型为何会导致难以发现的bug?如何利用TypeScript构建可靠的大型应用?
深度技术:
TypeScript作为JavaScript的超集,通过静态类型检查提供了更安全的开发体验。它不仅能捕获常见错误,还能增强代码的可读性和IDE的智能提示。
TypeScript的高级类型系统支持泛型、联合类型、交叉类型、条件类型等,能够精确建模复杂的业务逻辑。理解这些类型概念,对于构建大型前端应用至关重要。
代码示例:
// 基础类型与接口
interface Disciple {name: string;power: number;skills: string[];transform?: (form: string) => boolean; // 可选方法
}// 实现接口
const sunWukong: Disciple = {name: "Sun Wukong",power: 100,skills: ["Shape-shifting", "Cloud-riding"],transform(form) {console.log(`Transformed into ${form}`);return true;}
};// 泛型:创建可复用的组件
interface Response<T> {data: T;status: number;message: string;
}// 泛型函数
function fetchData<T>(url: string): Promise<Response<T>> {return fetch(url).then(response => response.json());
}// 使用泛型
interface User {id: number;name: string;
}fetchData<User>("/api/user/1").then(response => {const user = response.data; // TypeScript知道user是User类型console.log(user.name);});// 联合类型与类型守卫
type MagicalItem = | { type: "weapon"; damage: number; name: string }| { type: "armor"; defense: number; name: string }| { type: "potion"; effect: "heal" | "strength"; value: number };// 类型守卫函数
function isWeapon(item: MagicalItem): item is { type: "weapon"; damage: number; name: string } {return item.type === "weapon";
}// 使用类型守卫
function useItem(item: MagicalItem) {console.log(`Using ${item.name}`);if (isWeapon(item)) {// TypeScript知道这里item是武器console.log(`Dealing ${item.damage} damage`);} else if (item.type === "armor") {console.log(`Adding ${item.defense} defense`);} else {// 穷尽性检查:TypeScript确保所有类型都被处理console.log(`Gaining ${item.effect} effect of ${item.value}`);}
}// 高级类型:映射类型与条件类型
// 将对象所有属性变为只读
type ReadOnly<T> = {readonly [P in keyof T]: T[P];
};const readOnlyWukong: ReadOnly<Disciple> = {name: "Sun Wukong",power: 100,skills: ["Shape-shifting"]
};// readOnlyWukong.power = 200; // 错误:无法分配到"power",因为它是只读属性// 条件类型:根据条件选择不同的类型
type ExtractPowerType<T> = T extends { power: infer P } ? P : never;// 从Disciple类型中提取power的类型
type PowerType = ExtractPowerType<Disciple>; // number// Utility类型组合使用
interface Quest {id: number;name: string;difficulty: "easy" | "medium" | "hard";rewards: {gold: number;experience: number;items?: string[];};
}// 只选取部分属性
type QuestSummary = Pick<Quest, "id" | "name" | "difficulty">;// 使所有属性可选
type PartialQuest = Partial<Quest>;// 创建不可变的深度只读对象
type DeepReadonly<T> = {readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};const immutableQuest: DeepReadonly<Quest> = {id: 1,name: "Journey to the West",difficulty: "hard",rewards: {gold: 5000,experience: 10000}
};// immutableQuest.rewards.gold = 6000; // 错误:无法分配到"gold",因为它是只读属性
🚀 第九难:V8优化 - 让代码如"筋斗云"般迅捷
问题:为什么看似等价的JavaScript代码,性能却天差地别?如何编写V8引擎最喜欢的代码?
深度技术:
JavaScript性能优化需要了解V8引擎的工作原理,包括JIT编译、隐藏类、内联缓存等概念。V8通过多层编译优化(Ignition解释器和TurboFan优化编译器),将JavaScript转换为高效的机器码。
编写V8友好的代码,关键在于保持对象形状稳定、避免类型变化、理解属性访问优化和合理使用内存。这些优化技巧可以使应用性能提升数倍。
代码示例:
// 对象形状(隐藏类)优化
// 糟糕的做法:动态添加属性,导致创建多个隐藏类
function BadMonkey(name) {this.name = name;// 后续动态添加属性if (name === 'Sun Wukong') {this.power = 100;} else {this.intelligence = 80;}
}// 优化做法:始终使用相同的属性初始化顺序
function GoodMonkey(name) {this.name = name;this.power = name === 'Sun Wukong' ? 100 : 60;this.intelligence = name === 'Sun Wukong' ? 90 : 80;
}// 函数优化
// 多态函数很难被优化
function polymorphicCalculate(obj) {// 这个函数被不同类型的obj调用return obj.value * 2;
}// 分离为单态函数更易优化
function calculateForNumber(num) {return num * 2;
}function calculateForObject(obj) {return obj.value * 2;
}// 避免重度依赖参数类型检查的函数
function badAdd(a, b) {if (typeof a === 'string' || typeof b === 'string') {return String(a) + String(b);}return a + b;
}// 数组优化
// 避免处理混合类型数组
const mixedArray = [1, 'two', {three: 3}, 4]; // 性能较差// 使用类型一致的数组
const numbersArray = [1, 2, 3, 4]; // 性能更好
const objectsArray = [{value: 1}, {value: 2}]; // 性能更好// 避免数组孔洞
const sparseArray = [];
sparseArray[0] = 1;
sparseArray[10] = 10; // 创建"稀疏"数组,性能较差// 性能测量示例
function benchmark(fn, iterations = 1000000) {const start = performance.now();for (let i = 0; i < iterations; i++) {fn();}return performance.now() - start;
}// 属性访问优化
const monkey = { name: 'Wukong', power: 100 };// 方式1:反复查找对象的属性(较慢)
function slowAccess() {let sum = 0;for (let i = 0; i < 1000; i++) {sum += monkey.power;}return sum;
}// 方式2:局部变量缓存(更快)
function fastAccess() {const power = monkey.power;let sum = 0;for (let i = 0; i < 1000; i++) {sum += power;}return sum;
}// try/catch的性能影响
function withTryCatch() {try {// 业务逻辑return process() + 1;} catch (e) {return 0;}
}// 优化:将try/catch移到外层,不要在热代码路径
function betterErrorHandling() {return process() + 1;
}function safeRun(fn) {try {return fn();} catch (e) {return 0;}
}// 使用
const result = safeRun(betterErrorHandling);// 内存优化:避免闭包捕获整个作用域
function createExpensiveClosures() {const hugeData = new Array(10000).fill('data');return function() {// 这个闭包捕获了hugeDatareturn hugeData.length;};
}// 优化:仅捕获必要的数据
function createEfficientClosures() {const hugeData = new Array(10000).fill('data');const length = hugeData.length;return function() {// 只捕获了lengthreturn length;};
}
取经感悟
JavaScript的九大心法,如同悟空的七十二变,学会了就能应对各种前端妖魔。从原型链的继承之道,到异步编程的筋斗云,再到V8引擎的性能优化,每一难都是修炼的重要台阶。
请记住,JavaScript的强大在于其灵活性,但灵活往往伴随着复杂。真正的大师不在于掌握所有API,而在于理解语言的核心机制,以不变应万变。
下一站,我们将跟随三藏法师踏入DOM的修行之路,面对更加接近实战的九道试炼。
你在JavaScript修行路上遇到过哪些难关?欢迎在评论区分享你的"心法秘籍"!
相关文章:
前端取经路——JavaScript修炼:悟空的九大心法
大家好,我是老十三,一名前端开发工程师。JavaScript如同孙悟空的七十二变,变化多端却又充满威力。本篇文章我将带你攻克JS中最令人头疼的九大难题,从闭包陷阱到原型链继承,从异步编程到性能优化。每个难题都配有实战代…...
从零实战:在Xilinx Zynq PS端移植VxWorks 6.9系统
一、环境准备与工具链搭建 1.1 硬件配置清单 开发板: Zynq-7000系列(推荐ZedBoard或ZCU102)调试工具: USB-JTAG调试器(如Xilinx Platform Cable USB II)存储介质: SD卡(建议Class 10以上)1.2 软件环境 工具版本作用Vivado2022.1FPGA硬件设计Vitis2022.1系统集成开发Wind…...
网工实验——RIP配置
网络拓扑图 配置 1.为每台设备配置ip地址 AR4 <Huawei>u t m <Huawei>sys [Huawei]sysname AR4 [AR4]int g0/0/0 [AR4-GigabitEthernet0/0/0]ip address 172.16.1.1 24 [AR4-GigabitEthernet0/0/0]q#下面配置换回口,模拟网 [AR4]int LoopBack 0 [AR4…...
前端流行框架Vue3教程:14. 组件传递Props效验
(4) 组件传递Props效验 Vue组件可以更细致地声明对传入的props的校验要求 ComponentA.vue <script> import ComponentB from ./ComponentB.vue; export default {components: {ComponentB},data() {return {title: 标题}} } </script> <template><h3&g…...
电子电器架构 --- 网关ECU中采用多CPU解决方案来实现网关功能
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…...
关于tftpboot的用法
TFTPBOOT 是一个常用于嵌入式系统或网络设备中的命令,用于通过 TFTP 协议从网络上启动操作系统镜像或引导文件。这个命令通常在设备启动时执行,允许设备通过网络从 TFTP 服务器下载启动镜像或其他必要的文件,而不需要从本地存储中启动。 一般…...
团队协作的润滑剂——GitHub与协作流程
各位代码界的社交恐惧症患者们,今天我们要聊的是如何假装自己很会团队协作——使用GitHub!这就像程序员版的"相亲平台",只不过在这里,你展示的不是自拍和收入,而是代码和commit记录(后者往往更令…...
数据库复习
DML操作包括: SELECT INSERT UPDATE DELETE MERGE 返回字符串长度:length() 查询记录:SELECT 增(INSERT)、删(DELETE)、改(UPDATE)、查(SELECT&#…...
AI与机器学习、深度学习在气候变化预测中的应用与实践
前言: 全球气候变化是现代社会面临的最重要的环境挑战之一,影响了气温、降水、海平面、农业、生态系统等多个方面。气候变化的驱动因素主要包括温室气体排放、气溶胶浓度、火灾频发、海冰融化、叶绿素变化、农业变化和生态环境变化等。这些因素在全球范围…...
Laravel 12 基于 EMQX 实现 MQTT 消息发送与接收
Laravel 12 基于 EMQX 实现 MQTT 消息发送与接收 要在 Laravel 12 中实现基于 EMQX 的 MQTT 消息发送与接收,你可以按照以下步骤操作: 1. 安装必要的依赖包 首先安装 MQTT 客户端库: composer require php-mqtt/client2. 配置 EMQX 连接 …...
论广告系统对存算分离架构的应用
辅助论点 辅助论点一:存算分离架构起源于数据库领域,并不是在线系统。 存算分离的架构源于Google的Spanner数据库,这个数据库采用了KV做存储层,OLAP做计算层的分离式设计,其目的是能快速伸缩计算资源,且节…...
create-vue搭建Vue3项目(Vue3学习2)
一、认识create-vue image.png 二、create-vue搭建Vue3项目 image.png image.png 依次执行npm install 和npm run dev即可运行项目 image.png image.png © 著作权归作者所有,转载或内容合作请联系作者 喜欢的朋友记得点赞、收藏、关注哦!!ÿ…...
NHDEEP档案管理系统功能介绍
NHDEEP档案管理系统单机版专注于提高档案管理效率,无需网络连接即可独立运作,确保数据的安全与私密性。无论是机关单位的常规档案工作,还是工程、基建项目的特殊档案管理需求,系统都能提供全面的解决方案。系统支持信创环境。 核心…...
【C++】C++中的命名/名字/名称空间 namespace
C中的命名/名字/名称空间 namespace 1、问题引入2、概念3、作用4、格式5、使用命名空间中的成员5.1 using编译指令( 引进整个命名空间) ---将这个盒子全部打开5.2 using声明使特定的标识符可用(引进命名空间的某个成员) ---将这个盒子中某个成员的位置打…...
游戏引擎学习第260天:在性能分析器中实现钻取功能
昨天那个帧内存满之后触发段错误实在没找到什么原因导致的 继续研究一下为什么导致的 内存不够进来释放frame 释放frame 应该会给DebugState->FirstFreeStoredEvent 赋值吧 这段宏定义: #define FREELIST_DEALLOCATE(Pointer, FreeListPointer) \if(Pointer) {…...
人工智能100问☞第15问:人工智能的常见分类方式有哪些?
目录 一、通俗解释 二、专业解析 三、权威参考 人工智能的常见分类方式包括:按智能水平(弱人工智能、通用人工智能、超级人工智能)、按技术原理(生成式AI、判别式AI、强化学习)、按功能目标(生成内容、优化决策)、按应用领域(自然语…...
JavaSE核心知识点01基础语法01-04(数组)
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 JavaSE核心知识点01基础语法01-04࿰…...
抖音代播领航者——品融电商(PINKROON)的运营实力与服务解析
抖音代播领航者——品融电商(PINKROON)的运营实力与服务解析 在兴趣电商高速发展的背景下,杭州品融品牌管理有限公司(PINKROON)凭借其全域增长方法论与抖音生态的深度布局,成为众多品牌首选的抖音代播服务商…...
LeetCode 790 多米诺和托米诺平铺 题解
对于本题不去看LeetCode的评论区和题解很难想到如何去dp,毕竟就算再怎么枚举也很难找到适用于面向结果的规律。所以对于题解我建议大家还是去看一下灵神给的题解,以下是灵神汇总的图,如果能看懂的话,对于解决题目有很大的帮助。 根…...
力扣-hot100 (缺失的第一个正数)
41. 缺失的第一个正数 困难 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1: 输入:nums [1,2,0] 输出:3 解释ÿ…...
Electrolink信息泄露(CVE-2025-28228)
免责声明 本文档所述漏洞详情及复现方法仅限用于合法授权的安全研究和学术教育用途。任何个人或组织不得利用本文内容从事未经许可的渗透测试、网络攻击或其他违法行为。使用者应确保其行为符合相关法律法规,并取得目标系统的明确授权。 对于因不当使用本文信息而造成的任何直…...
Leetcode Hot 100 三数之和
思路 对数组先排序,然后使用双指针法进行,并且整个过程需要把握去重的逻辑 代码 class Solution:def threeSum(self, nums: List[int]) -> List[List[int]]:if not nums:return []nums.sort() #去重都需要排序res[]for i in range(len(nums)):if i…...
5月6日日记
一点心得是 看通知要仔细认真,自己想问的问题要先看看通知或者文件中说了没有,如果没说再去问相关负责人。 上课的教室一定要看好,看准了再去。别像今天一样先去了科技楼又去了工学馆。 线代开课了。感觉总体还行,并不是很难。…...
巧记英语四级单词 Unit7-中【晓艳老师版】
collapse v.倒塌,坍塌 都(col)来扑(lap)噻immune a.不受影响的,免疫的 我im 木讷mune,人应该木讷一点yard n.院子 鸭的,在哪养;backyard 后院backward a.往后的 ward表示方向 profile n.外形,轮廓 从前面看…...
Windows系统修改Docker Desktop(WSL2)内存分配
# Windows查看docker信息 docker info 新增wsl全局配置文件(.wslconfig文件):windows路径栏输入:%UserProfile%,找到目录C:\Users\Administrator,默认是没有这个配置文件的,可以自己新增 # 设置在wsl2上运行 [wsl2] # …...
Oracle02-安装
零、文章目录 Oracle02-安装 1、Windows Server2022安装Oracle11g (1)下载 百度网盘地址: https://pan.baidu.com/s/15MBkMt1ldbSFm4L74h7Myg?pwd8888下载完成两个压缩包解压放在一起 (2)安装 双击 setup 文件安…...
Linux[Makefile]
Makefile基础结构 规则语法 target: prerequisitescommandtarget:生成的目标(如可执行文件、.o文件) prerequisites:依赖项(源码、头文件等) command:构建命令(必须用Tab缩进&am…...
相同的数(简单)
深度优先搜索 如果两个二叉树都为空,则两个二叉树相同。如果两个二叉树中有且只有一个为空,则两个二叉树一定不相同。 如果两个二叉树都不为空,那么首先判断它们的根节点的值是否相同,若不相同则两个二叉树一定不同,…...
「Mac畅玩AIGC与多模态22」开发篇18 - 多段输出拼接与格式化展现工作流示例
一、概述 本篇以已有多字段输出为基础,介绍如何通过执行 LLM 节点对多个上游字段进行统一拼接与格式化处理。开发人员将学习如何从多个节点输出中提取数据字段,并组合为结构清晰、风格统一的最终输出,提升用户阅读体验。 二、环境准备 mac…...
餐饮部绩效考核管理制度与综合评估方法
在竞争激烈的餐饮行业中,标准化与数据驱动的管理手段正成为提升服务质量与运营效率的关键。绩效考核不仅关乎员工奖惩,更直接影响顾客体验、成本控制与营收水平。构建一套科学有效的绩效体系,是餐饮部精细化运营的起点。 本文围绕餐饮部绩效…...
conda虚拟环境相关操作
查看当前存在哪些虚拟环境 conda env list conda info --env创建虚拟环境conda create -n env_name pythonX.X删除虚拟环境conda remove -n env_name --all查看安装了哪些包conda list下载/删除环境中的某个包conda install package_nameconda uninstall package_name删除所有未…...
达梦DM数据库安装步骤
文章目录 1、下载并解压缩2、安装DM数据库2.1 运行安装程序2.2 选择语言与时区2.3 安装向导2.4 许可证协议2.5 Key文件2.6 选择组件2.7 安装位置2.8 安装前小结2.9 安装过程2.10 已完成2.11 初始化 3、配置实例3.1选择操作方式3.2创建数据库模版3.3指定数据库目录3.4数据库标识…...
vue3在使用@import “./index.scss“报错
Deprecation Warning: Sass import rules are deprecated and will be removed in Dart Sass 3.0.0. More info and automated migrator: https://sass-lang.com/d/import 2 │ import "./index.scss"; 在 Sass 3.0.0.之后 导入样式使用 “use” ,不在使…...
对标研华ECU-461,搭载飞腾4核/8核国产处理器, 提供8网 8串B码对时 双显 无风扇的ARM通信管理平台
飞腾 FT-2000/4 和 D2000/8 主控制器,主频 2.3~2.6GHz 8 个千兆网口 , 8 个全功能隔离串口 HDMIVGA 双显示接口 3 个 USB2.0, 2 个 USB3.0 支持 2 组 SATA 硬盘存储 支持 CAN 通讯 ( 替换 4 路或 8 路 COM) 整机无风扇散热设计 …...
如何将C#程序打包成软件绿色包
文章目录 前言步骤如下:总结 前言 在实际工作中,很多时候会开发一些特别小的工具,当这些工具需要发给别人用时,不值当的打个安装包,最适合做一个绿色包,别人拿到后,直接双击exe就可以用。 步骤…...
实验三 数据查询
一、【实验教学 1、掌握单表查询。 2、掌握多表查询。 二、【实验教学的基本要求】 1、掌握SQL程序设计基本规范; 2、熟练运用SQL实现数据基本查询,包括单表查询、分组统计查询和连接查询; 3、理解和掌握SQL查询语句中各个子句的特点和…...
关于串口读写NAND闪存的用法
在嵌入式系统中,nand 命令通常用于操作和管理 NAND 闪存子系统,特别是在引导加载程序(如 U-Boot)中。NAND 闪存是一种非易失性存储设备,广泛用于嵌入式设备中,用于存储操作系统、应用程序、配置文件等数据。…...
C++:实现线程池
线程池(Thread Pool)是一种多线程处理方式,用于管理和复用多个线程,以提高程序的并发性能并避免频繁创建和销毁线程所带来的开销。 基本概念: 线程池维护着若干个已创建好的线程,当有任务需要执行时&…...
本地运行qwen3:30b-a3b速度测试
仍然使用的是ollama,运行的Q4_K_M量化版。 这个模型在相同硬件环境下对比我电脑上其他32b的模型速度(小于3 tokens/s)提升非常明显,并且可以设置是否打开思考模式。 注意: /no_think前有个空格 非思考模式࿱…...
keil+vscode+腾讯ai助手
嵌入式软件开发 这个是之前一直想写的开发方式,不过上份工作一直在忙,没有抽出时间花在上面,现在空下来好好写一写吧!标题软件安装 关于VSCode以及Keil的安装可以在以下链接中点击浏览 VSCode安装 Keil5安装 CubeMx安装 插件下…...
通过TinyML为语音助手赋能,推动以用户为中心的创新和现实世界应用
英文标题:Empowering voice assistants with TinyML for user-centric innovations and real-world applications 中文标题:通过TinyML为语音助手赋能,推动以用户为中心的创新和现实世界应用 作者信息 Sireesha Chittepu1, Sheshikala Mart…...
学习Python网络爬虫的实例
30岁程序员学习Python的第二天之网络爬虫的练习实例 爬取软科2025年中国大学排名 思路: 1、百度查到到网页地址:https://www.shanghairanking.cn/rankings/bcur/2025 2、编写爬取代码,具体步骤分3步,第一步通过requests库爬取网…...
雨云游戏云MCSM面板服使用教程我的世界Forge服务端开服教程
雨云 - 新一代云服务提供商 雨云面板服目前支持一键开服的游戏有:Minecraft Java版、Minecraft 基岩版、泰拉瑞亚、饥荒,还提供纯Java/Linux环境(Docker),方便开自己开其他游戏服。 其中Minecraft Java版支持一键开…...
关于loadstartcode使用
loadstartcode 命令用于从 TFTP 服务器下载一个名为 startcode 的文件。这个命令通常用于将启动代码(如引导加载程序或内核启动映像)从 TFTP 服务器加载到设备内存中。它是嵌入式设备和网络设备(如路由器)常见的命令,通…...
Linux死锁实验分析与总结
三、实验结果截图及分析 1. 实验代码 #include <pthread.h> #include <stdio.h> #include <unistd.h>pthread_mutex_t mutex1 PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex2 PTHREAD_MUTEX_INITIALIZER;void* producer(void* arg) {while (1) {pth…...
【计网】ipconfig、ping、arp、tracert
目录 ipconfig ping arp tracert cmd ipconfig ipcofig -all IPv4 物理地址 ping 检测网络连通情况,分析网络速度 根据域名得到服务器IP 根据TTL判断对方所使用的操作系统以及数据包经过路由器数量 byte数据包大小 time响应时间 TTLDNS记录在DNS服务器上存在…...
当手机开始预判你的下一步:一场正在颠覆生活的AI静默革命
当手机开始预判你的下一步:一场正在颠覆生活的AI静默革命 深夜加班时,手机自动调暗屏幕亮度;出差途中,智能音箱提前预定好常去的酒店;打开购物APP,推荐清单里躺着昨天刚在聊天中提到的商品——这些场景背后…...
【SDRS】面向多模态情感分析的情感感知解纠缠表征转移
abstract 多模态情感分析(MSA)旨在利用多模态的互补信息对用户生成的视频进行情感理解。现有的方法主要集中在设计复杂的特征融合策略来整合单独提取的多模态表示,忽略了与情感无关的信息的干扰。在本文中,我们提出将单模表征分解为情感特定特征和情感独立特征,并将前者融…...
C++ 中的静态链接和动态链接详解
目录 一、什么是链接? 链接分为两类: 二、静态链接(Static Linking) 特点: 优点: 缺点: 使用方式: 三、动态链接(Dynamic Linking) 特点: 优…...
426、N叉树的层序遍历
输入检查: if not root:return [] 如果根节点为空,直接返回空列表 初始化: result [] queue collections.deque([root]) result用于存储最终结果queue初始化包含根节点,使用双端队列实现 主循环: while queue:leve…...