LeetCode 的 JavaScript 时代实际上填补了空白
哈喽!今天心血来潮给大家带来了《LeetCode 的 JavaScript 时代实际上填补了空白》,想必大家应该对文章都不陌生吧,那么阅读本文就都不会很困难,以下内容主要涉及到,若是你正在学习文章,千万别错过这篇文章~希望能帮助到你!

大多数编码挑战都会教你解决难题。 leetcode 的 30 天 javascript 学习计划做了一些不同的事情:它向您展示了拼图如何变成砖块,准备好构建现实世界的项目。
这种区别很重要。当您解决典型的算法问题时,您正在训练您的思维进行抽象思考。但是,当您实现去抖1函数或构建事件发射器2时,您正在学习真正的软件是如何工作的。
我自己在应对挑战时发现了这一点。这种体验不太像解决脑筋急转弯问题,而更像考古学——揭示特定的现代 javascript 概念。每个部分都重点介绍 js 的另一项现代功能。
这个学习计划的奇特之处在于它不会教你 javascript。事实上,我相信您需要相当了解 javascript 才能从中受益。相反,它教的是如何使用 javascript 来解决实际的工程问题。
考虑 memoize3 挑战。从表面上看,它是关于缓存函数结果的。但你真正学到的是为什么像 react 这样的库需要记忆来有效地处理组件渲染。或者以 debounce1 问题为例 - 这不仅仅是实现延迟;它还涉及延迟。它可以帮助您直接理解为什么每个现代前端框架、电梯以及基本上任何具有交互式 ui 的系统都需要这种模式。
这种对实用模式而不是语言基础知识的关注创造了一个有趣的限制;您需要处于以下两个位置之一才能受益:
- 您了解 cs 基础知识(尤其是数据结构和算法)并且熟悉 javascript
- 您的计算机科学理论很强,并且之前接触过一些 javascript
连接计算机科学和软件工程
学习计算机科学和实践软件工程之间会发生一些奇怪的事情。这种转变感觉就像学习了多年的国际象棋理论,却发现自己在玩一种完全不同的游戏 - 规则不断变化,而且大多数动作都不在任何书本中。
在计算机科学中,您将学习二叉树的工作原理。在软件工程中,您需要花费数小时调试 api,试图了解响应缓存不起作用的原因。从远处看,这些世界之间的重叠可能看起来比实际要大得多。其中存在差距,这常常会让计算机科学毕业生在开始职业生涯时感到震惊。不幸的是,大多数教育资源都无法弥补这一点。它们要么纯粹是理论性的(“这是快速排序的工作原理”),要么是纯粹的实用性(“这是如何部署 react 应用程序”)。
这个 javascript 学习计划的有趣之处并不是它设计得特别好 - 而是它在这些世界之间创建了联系。以记忆问题为例:2623。记忆3。用计算机科学术语来说,它是关于缓存计算值的。但实现它会迫使您应对 javascript 在对象引用、函数上下文和内存管理方面的特殊性。突然,
您不仅仅是在学习算法 - 您开始理解为什么像 redis 这样的东西存在。
这种风格在整个挑战中不断重复。 event emitter2 实现不仅仅是教科书观察者模式 - 您可以将其视为将 v8 引擎从浏览器中取出并围绕它构建 node.js 的原因,这实际上是有意义的。 promise pool4 解决并行执行问题,也就是数据库需要连接限制的原因。
隐藏课程
本学习计划中的问题顺序不是随机的。它正在逐层构建现代 javascript 的心理模型。
它从闭包开始。并不是因为闭包是最简单的概念 - 它们非常令人困惑 - 而是因为它们是 javascript 管理状态的基础。
function createcounter(init) {
let count = init;
return function() {
return count++;
}
}
const counter1 = createcounter(10);
console.log(counter1()); // 10
console.log(counter1()); // 11
console.log(counter1()); // 12
// const counter1 = createcounter(10);
// when this^ line executes:
// - createcounter(10) creates a new execution context
// - local variable count is initialized to 10
// - a new function is created and returned
// - this returned function maintains access
// to the count variable in its outer scope
// - this entire bundle
// (function (the inner one) + its access to count)
// is what we call a closure
这种模式是 javascript 中所有状态管理的种子。一旦你了解了这个计数器的工作原理,你就了解了 react 的 usestate 在幕后是如何工作的。您了解为什么模块模式出现在 es6 之前的 javascript 中。
然后计划转向功能转换。这些教你函数装饰——函数包装其他函数以修改它们的行为。这不仅仅是一个技术技巧;这就是 express 中间件的工作方式,react 高阶组件的工作方式,
以及 typescript 装饰器的工作原理。
当您遇到异步挑战时,您不仅学习了 promise,而且还发现了 javascript 最初需要它们的原因。 promise pool4 问题并不是在教你一个创新的、古怪的 js 概念;而是在教你一个创新的、古怪的 js 概念。它向您展示了为什么每个数据库引擎中都存在连接池。
以下是学习计划各部分与现实世界软件工程概念的粗略映射:
- 关闭 → 状态管理
- 基本数组变换 → 基本技能(辅助);实际示例:数据操作
- 函数转换 → 中间件模式
- 承诺和时间 -> 异步控制流程
- json -> 基本功(辅助);实例:数据序列化、api通信
- 类(特别是在事件发射器的上下文中)→消息传递系统
- 奖励(高级锁定)-> 可能包含在上述部分中的更难挑战的混合; promise pool4 是本节中我最喜欢的一个
模式识别,而不是解决问题
让我们剖析一些问题,以展示该学习计划的真正价值。
- 记忆 (#2623)
考虑 memoize 挑战。我喜欢它的原因是(我能想出的)最好的解决方案
非常简单,就好像代码本身在温和地告诉您它的作用(不过,我添加了一些注释)。
无论如何,这并不会让 #2623 成为一个简单的问题。我之前需要两次迭代才能使它变得如此干净:
/**
* @param {function} fn
* @return {function}
*/
function memoize(fn) {
// create a map to store our results
const cache = new map();
return function(...args) {
// create a key from the arguments
const key = json.stringify(args);
// if we've seen these arguments before, return cached result
if (cache.has(key)) {
return cache.get(key);
}
// otherwise, calculate result and store it
const result = fn.apply(this, args);
cache.set(key, result);
return result;
}
}
const memoizedfn = memoize((a, b) => {
console.log("computing...");
return a + b;
});
console.log(memoizedfn(2, 3)); // logs "computing..." and returns 5
console.log(memoizedfn(2, 3)); // just returns 5, no calculation
console.log(memoizedfn(3, 4)); // logs "computing..." and returns 7
// explanantion:
// it's as if our code had access to an external database
// cache creation
// const cache = new map();
// - this^ uses a closure to maintain the cache between function calls
// - map is perfect for key-value storage
// key creation
// const key = json.stringify(args);
// - this^ converts arguments array into a string
// - [1,2] becomes "[1,2]"
// - we are now able to use the arguments as a map key
// cache check
// if (cache.has(key)) {
// return cache.get(key);
// }
// - if we've seen these arguments before, return cached result;
// no need to recalculate
- 反跳 (#2627)
想象一下你在电梯里,有人疯狂地反复按“关门”按钮。
按 按 按 按 按
没有去抖:电梯会在每按一次门时尝试关门,导致门机构工作效率低下,甚至可能损坏。
防抖:电梯会等待,直到人停止按下一定时间(假设 0.5 秒),然后才真正尝试关门。这样效率就高多了。
这是另一种情况:
假设您正在实现一个搜索功能,可以在用户键入时获取结果:
没有去抖动:
// typing "javascript" 'j' -> api call 'ja' -> api call 'jav' -> api call 'java' -> api call 'javas' -> api call 'javasc' -> api call 'javascr' -> api call 'javascri' -> api call 'javascrip' -> api call 'javascript' -> api call
这将进行 10 次 api 调用。其中大多数都没用,因为用户仍在打字。
带去抖动(300 毫秒延迟):
// typing "javascript" 'j' 'ja' 'jav' 'java' 'javas' 'javasc' 'javascr' 'javascri' 'javascrip' 'javascript' -> api call (only one call, 300ms after user stops typing)
去抖就像告诉你的代码:“等到用户停止做某事 x 毫秒后再实际运行这个函数。”
这是 leetcode #2627 的解决方案:
/**
* @param {Function} fn
* @param {number} t milliseconds
* @return {Function}
*/
var debounce = function (fn, t) {
let timeoutID = null;
return function (...args) {
clearTimeout(timeoutID); // MDN reference: https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout
timeoutID = setTimeout(() => {
fn.apply(this, args);
}, t);
};
};
// tests
const log = debounce(console.log, 100);
console.log("Starting tests...");
setTimeout(() => log("Test 1"), 50); // should be cancelled
setTimeout(() => log("Test 2"), 75); // should be cancelled
setTimeout(() => log("Test 3"), 200); // should be logged at t=300ms
setTimeout(() => {
console.log("Tests completed.");
}, 400);
// Explanantion:
// - we create a closure where timeoutID persists between calls
// - timeoutID starts out as null
// - we return the debounced version of the initial function
// const log = debounce(console.log, 100);
// - this^ creates a new closure with its own timeoutID
// - log is now the inner function
// setTimeout(() => log("Test 1"), 50); // at t=50ms
// setTimeout(() => log("Test 2"), 75); // at t=75ms
// setTimeout(() => log("Test 3"), 200); // at t=200ms
// t=50ms:
// - log("Test 1") called
// - clears timeoutId (null, so nothing happens)
// - sets new timeout to run at t=150ms
// t=75ms:
// - log("Test 2") called
// - clears previous timeout (cancels "Test 1")
// - sets new timeout to run at t=175ms
// t=200ms:
// - log("Test 3") called
// - clears previous timeout (cancels "Test 2")
// - sets new timeout to run at t=300ms
// t=300ms:
// - finally executes fn("Test 3")
// why this works:
// - the closure keeps timeoutId alive between calls
// - every new call cancels the previous timeout
// - only the last scheduled timeout actually runs
现实世界中其他常见的去抖动用例(搜索栏除外):
- 保存草稿(等待用户停止编辑)
- 提交按钮(防止重复提交)
出了什么问题
我希望,从这篇文章整体积极的基调来看,我对js 30天的看法现在已经清晰了。
但是没有任何教育资源是完美的,并且当涉及到局限性时,诚实是有价值的。这个学习计划有几个盲点值得审视。
首先,学习计划假设有一定水平的先验知识。
如果您还不太熟悉 javascript,那么某些挑战可能会令人难以承受。对于可能对学习计划抱有其他期望的初学者来说,这可能会令人沮丧。
其次,挑战是以孤立的方式呈现的。
这在一开始是有道理的,但随着计划的进展,你可能会感到失望。现实世界的问题通常需要结合多种模式和技术。研究计划可以受益于需要一起使用多个概念的更综合的挑战(例外:我们在整个计划中都使用了闭包)。这些很适合放在奖励部分(已经为高级用户保留)。
最后,这组挑战的主要弱点在于其概念解释。来自竞争性节目,
我习惯于在问题陈述中明确新术语和概念的定义。然而,leetcode 的描述通常过于复杂——理解他们对去抖函数的解释比实现实际的解决方案更难。
尽管有缺点,该学习计划仍然是理解现代 javascript 的宝贵资源。
超过 30 天
理解这些模式只是一个开始。
真正的挑战是识别何时以及如何将它们应用到生产代码中。这是我在野外遇到这些模式后发现的。
首先,这些模式很少单独出现。真正的代码库以挑战无法探索的方式将它们组合起来。考虑一个从头开始实现的搜索功能。您可能会发现自己使用:
- 输入处理的反跳
- 结果缓存的记忆
- 承诺 api 调用超时
- 用于搜索状态管理的事件发射器
所有这些模式相互作用,创造了复杂性,没有任何单一的挑战能让您做好准备。但是,在自己实现了每个部分之后,您就会大致了解整个实现的运作方式。
与直觉相反,您将获得的最有价值的技能不是实现这些模式 - 而是在其他人的代码中识别它们。
最后的想法
完成本学习计划后,编程面试并不是您认识这些模式的唯一地方。
您会在开源代码、同事的拉取请求中发现它们,并且可能会开始在您过去的项目中注意到它们。您以前可能已经实现了它们,甚至没有意识到。最重要的是,您会明白它们为何存在。
最初的解谜转变为对现代 javascript 生态系统的更深入理解。
这就是本学习计划填补的空白:将理论知识与实践工程智慧联系起来。
-
2627。 debounce(承诺与时间)↩
-
2694。事件发射器(类)↩
-
2623。 memoize(函数转换)↩
-
2636。承诺池(奖金)↩
今天带大家了解了的相关知识,希望对你有所帮助;关于文章的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~
西电大:一种具有带宽扩展和失配抑制性能的紧凑Vivaldi天线
- 上一篇
- 西电大:一种具有带宽扩展和失配抑制性能的紧凑Vivaldi天线
- 下一篇
- OpenAI 近 400 名员工迎来股票套现机会,每人最多出售 1000 万美元
-
- 文章 · 前端 | 1小时前 |
- Flex布局order和align-self实战技巧
- 274浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- CSS设置元素宽高方法详解
- 359浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- JavaScript宏任务与CPU计算解析
- 342浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- float布局技巧与应用解析
- 385浏览 收藏
-
- 文章 · 前端 | 1小时前 | JavaScript模块化 require CommonJS ES6模块 import/export
- JavaScript模块化发展:CommonJS到ES6全解析
- 192浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- jQueryUI是什么?功能与使用详解
- 360浏览 收藏
-
- 文章 · 前端 | 2小时前 |
- 搭建JavaScript框架脚手架工具全攻略
- 149浏览 收藏
-
- 文章 · 前端 | 2小时前 | JavaScript Bootstrap 响应式设计 CSS框架 Tab切换布局
- CSS实现Tab切换布局教程
- 477浏览 收藏
-
- 文章 · 前端 | 2小时前 |
- 并发控制:限制异步请求数量方法
- 313浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3180次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3391次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3420次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4526次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3800次使用
-
- JavaScript函数定义及示例详解
- 2025-05-11 502浏览
-
- 优化用户界面体验的秘密武器:CSS开发项目经验大揭秘
- 2023-11-03 501浏览
-
- 使用微信小程序实现图片轮播特效
- 2023-11-21 501浏览
-
- 解析sessionStorage的存储能力与限制
- 2024-01-11 501浏览
-
- 探索冒泡活动对于团队合作的推动力
- 2024-01-13 501浏览

