Levix 空间站
920 subscribers
219 photos
11 videos
20 files
1.38K links
主要分享前端、AI 以及前沿科技资讯。

🚫 禁止人身攻击:请在评论区保持尊重和友好,避免不当言论和负面互动。

🚫 禁止违规内容:请勿发布任何黄赌毒、宗教极端、政治敏感或其他违反社区规定的内容。
主要分享前端以及业界科技资讯。

🚫 禁止广告与刷屏:为了维护良好的交流环境,请不要进行任何形式的广告推广、黑产活动、刷屏行为及发布不适内容。

🔒 保护个人信息:请注意个人隐私和网络安全,不要在评论区泄露个人信息或点击不明链接。
Download Telegram
JavaScript 日期处理即将迎来重大改进,其中最引人注目的是 Temporal 提案。该提案通过 FullCalendar 团队提供的 polyfill 已经可以提前使用。Temporal API 的一大优势是引入了原生的 "带时区的日期时间(Zoned Date Time)" 对象。

在人类交流中,日期通常不包含时区信息,例如 "2024年8月4日上午10:30"。但计算机处理 JavaScript 的 Date 对象时,实际上是处理纯数字,这导致日期的原始语义丢失。JavaScript 中的日期实际上是 POSIX 时间(忽略闰秒),而非 UTC 时间。

Temporal API 引入了 Temporal.ZonedDateTime 对象,专门用于表示带有对应时区的日期和时间。例如,一个时间戳可以对应多个人类可读的日期,这取决于时区。例如,同一个时间戳在澳大利亚、马德里和美国可能对应不同的当地时间。

Temporal API 的优势

1. 创建日期:Temporal API 在创建日期时可以轻松处理时区,包括夏令时(DST)的棘手情况。

2. 日期比较:ZonedDateTime 提供了静态方法 compare,可以比较两个日期。

3. 内置属性:如 hoursInDay 属性,可以返回特定时区中一天的实际小时数。

4. 时区转换:通过 .withTimeZone 方法轻松改变 ZonedDateTime 的时区。

5. 日期运算:支持日历算术或简单持续时间的加减,自动调整夏令时。

#JavaScript #ECMAScript #新特性 #Date

https://docs.timetime.in/blog/js-dates-finally-fixed/
JavaScriptsetTimeout 函数用于延迟执行代码,但存在一个限制:在大多数 JavaScript 运行时环境中,其超时时间被表示为一个 32 位有符号整数,这意味着最大超时时间约为 2.1 亿毫秒,或约 24.9天。超过这个时间限制,尝试设置更大的超时会导致函数立即执行,这在某些极端情况下是不可接受的。

为了解决这个问题,开发者 Evan Hahn 创建了一个名为 setBigTimeout 的模块。这个模块的工作方式与 setTimeout 类似,但能够处理非常大的延迟时间。它通过将多个较短的超时时间(每个都在限制范围内)串联起来,来实现这一点。例如,可以使用 setBigTimeout 来设置长达 84 年的延迟,甚至可以使用 bigint 类型来设置更长的延迟时间。

setBigTimeout 模块可以通过 npm 安装,也可以查看其源代码。开发者还编写了自动化测试来模拟时钟,但并没有真正等待 84 年来验证其有效性。感兴趣的开发者可以尝试使用这个模块,以解决长时间延迟的需求。

#Tools #JavaScript

https://evanhahn.com/set-big-timeout/
Nolan Lawson 表达了对将 JavaScript 工具用所谓“更快”的语言重写持怀疑态度。尽管 Rust、Zig、Go 等语言有其优势,但他对 JavaScript 有深厚的情感,因为他在 JavaScript 的理解、优化和调试方面投入了大量的时间和精力。 Lawson 认为 JavaScript 工具的性能提升空间尚未完全挖掘,例如 Marvin Hagemeister 展示的 ESLint 和 Tailwind 中的性能提升潜力。在浏览器中 JavaScript 已经足够快,而 WebAssembly 主要用于特定的 CPU 密集型任务。

新工具(如 Rolldown、Oxlint、Biome)可能因为性能考量和 API 设计的成熟而更快,并非仅仅因为使用了更快的语言。Lawson 引用了 Ryan Carniato 的观点,即重写本身可能带来性能提升,因为开发者在重写时更加关注性能。 Node.js 脚本缺乏浏览器提供的字节码缓存和 JIT 编译器的优势,这可能是 JavaScript 工具与非 JavaScript 工具性能差异的一个原因,Node.js 正在获得编译缓存功能,这可能会改善性能。

JavaScript 是一种易于学习和调试的语言,拥有庞大的开发者社区。如果工具由更难的语言编写,这可能会增加调试和修改的难度,从而影响社区的贡献。他认为,JavaScript 是一种“工薪阶层”的语言,它对类型宽容,易于上手,并且由于浏览器的支持,有大量的人熟悉它。如果 JavaScript 库的作者使用不同于 JavaScript 的语言,这可能会使得开发者感到无助。

尽管新一代工具的出现是好事,但他不认为 JavaScript 本质上慢,或者我们已经穷尽了改进 JavaScript 的可能性。如果 JavaScript 工具开发变成只有少数 Rust 和 Zig 开发者能理解的领域,可能会让普通开发者感到无助。他呼吁社区考虑重写工具的长远影响,并思考是否有更少风险、更接近相同结果的路径。 Lawson 表达了对 JavaScript 未来的担忧,认为我们可能正走向一个未知的道路,而这条路可能带来意想不到的后果。

#JavaScript #思考

https://nolanlawson.com/2024/10/20/why-im-skeptical-of-rewriting-javascript-tools-in-faster-languages/
BBC 的导航栏组件出现了一个只在特定条件下才会触发的 bug,这个 bug 只在一名团队成员使用家中的外部显示器时出现。当这名成员点击 “更多” 按钮时,按钮并没有打开菜单,而是触发了无 JavaScript 的备用行为。这个问题在办公室或使用笔记本电脑屏幕时并不会出现,而且只在 Chrome 和 Firefox 浏览器中出现,Safari 浏览器则不受影响。

经过调查,发现这个 bug 与多显示器的设置有关。当外部显示器被设置为主显示器的上方或左侧时,Chrome 和 Firefox 在处理屏幕坐标时会出现负值,这导致点击事件无法被正确识别。BBC 的代码原本假设所有的点击事件都会有正的屏幕坐标值,这就导致了问题的出现。

为了解决这个问题,团队修改了代码中的事件处理逻辑,不再检查屏幕坐标是否大于 0,而是检查它们是否不等于 0。这个简单的改变让导航栏在各种多显示器布局下都能正常工作了。

尽管问题得到了解决,但团队意识到依赖屏幕坐标值并不是一个好方法。他们计划对事件处理函数进行进一步的重构,以避免未来出现类似的问题。这个经历也提醒了开发者,在开发中不能想当然地假设 API 的行为,即使经过了严格的测试,也可能因为一些特殊的环境设置而出现问题。未来,团队还会分享导航组件是如何被重构的,以及回答一些大家可能会有的问题。

#前端 #JavaScript

https://www.joshtumath.uk/posts/2024-11-08-how-a-bbc-navigation-bar-component-broke-depending-on-which-external-monitor-it-was-on/
Porffor 是一个预编译 JavaScript 编译器,它将 JavaScript 代码编译成 WebAssembly 和原生二进制文件。目前该项目处于预 alpha 阶段,预计从 2025 年开始具备可用性。Porffor 的 WebAssembly 输出比现有的 JS 到 Wasm 项目小 10-30 倍且更快,因为它直接编译 JS,而不是在 Wasm 输出中打包解释器。这使得 Porffor 能够解决 JS 作为 Wasm 时性能损失严重的问题,允许高效的、安全的服务器端 JS 托管,并且对抗逆向工程的能力更强。对于 JS 到原生代码的编译,Porffor 不打包运行时环境,因此二进制文件大小小 1000 倍(从约 90MB 减少到小于 100KB),并且可能还有其他性能优势。这使得 JS 可以用于嵌入式设备、游戏控制台等多种场景,并且可以编写极小的 CLI 应用程序。

Porffor 的安全性体现在它完全用 JS 编写(避免了内存安全漏洞),并且不使用 eval。它是从头开始编写的,以 AOT(Ahead-of-Time)编译为目标,允许进行以前不可能的优化。此外,Porffor 原生支持 TypeScript,不需要任何转译步骤,直接提供 TS 文件即可。

在编译时,Porffor 与传统的嵌入式编译器不同,它直接将 JavaScript 编译成二进制(Wasm / 原生),而不是生成字节码。在运行时,与传统的解释器和 JIT 编译器不同,Porffor 在编译时就完成了所有工作,因此运行时可以直接执行编译后的代码,无需解释或 JIT 编译,这使得 Porffor 可以进行静态分析并优化性能,类似于 C++ 或 Rust。

然而,AOT 的主要缺点是运行时 JS 执行(如 eval、new Function 等)是不可行的。由于 Porffor 还处于早期阶段,它不稳定,并不是所有的 JS 代码都能工作。目前,Porffor 在 Test262(官方 ECMAScript 一致性测试套件)中的通过率为 50.96%。

#Wasm #JavaScript

https://github.com/CanadaHonk/porffor
1
Boa JS 是一个用 Rust 语言编写的 ECMAScript 引擎,它将 Rust 的内存安全特性带入了 JavaScript 引擎的世界。Boa 已通过了超 80% 的 ECMAScript 测试。

#Rust #JavaScript

https://github.com/boa-dev/boa
深入探讨了并发与并行的区别,并以 JavaScript 和 Node.js 为例,解释了它们在编程中的实现方式及其对性能和问题解决的影响。

并发和并行经常被交替使用,但实际上它们是不同的概念。并发指的是通过交替执行多个任务的子任务(即交错执行)来处理多个任务,而并行则是同时执行多个任务。例如,一个人先看手机,然后放下手机去喝汤,再回到手机上,这就是并发工作。相比之下,如果一个人一边发短信一边吃饭,这就是并行工作。

在编程中,子任务可以被视为更大一组指令中的个别部分。传统的同时操作不同子任务的方法是创建不同的内核线程。内核线程类似于独立的工作者,每个都处理特定的任务,同时也能操作同一套指令和资源。线程是并发或并行运行实际上取决于硬件。如果 CPU 的核心数多于同时运行的线程数,每个线程可以分配到不同的核心上,允许它们并行运行。但如果 CPU 的核心数少于线程数,操作系统将开始在线程之间交错执行。

除了使用线程外,还有其他方式可以实现并发 / 并行,例如生成多个进程。由于 CPU 可以并行和并发运行不同进程,可以使用多个进程进行多任务处理。缺点是每个进程都有自己的内存空间分配,它们默认不像线程那样共享内存空间。因此,如果需要不同进程操作同一状态,可能需要某种 IPC 机制,如共享内存段、管道、消息队列甚至数据库。

编程语言通常提供自己的并发机制,以简化与操作系统 API(系统调用)相关的复杂性。Node.js 就是一个很好的例子。尽管 JavaScript 程序在单线程环境中以顺序执行流程运行,但诸如 IO 操作等阻塞任务被委托给 Node.js 工作线程。Node.js 在后台使用线程来管理这些阻塞任务,而不向开发者展示管理它们的复杂性。

Node.js 通过回调函数处理阻塞操作,如文件读写或网络请求。开发者在调用这些函数时通常传递回调函数作为参数,以便 Node.js 工作线程在完成任务后执行提供的回调函数。Node.js 的主代码可以被视为默认回调。一旦主代码中的所有指令执行完毕,Node.js 运行时环境就开始调用回调函数。

在并发 / 并行程序中工作时,需要小心,因为事情可能很快就会出错。并发编程可能会减少诸如竞态条件等问题的发生,但仍然需要避免复杂的逻辑,依赖于 async 函数和嵌套回调。

#JavaScript

https://www.rugu.dev/en/blog/concurrency-and-parallelism/
Felix Rieseberg 针对 Electron 框架的一些常见误解进行了澄清。Electron 是一个用于构建跨平台桌面应用程序的开源框架,它结合了 Chromium 浏览器引擎和 Node.js,允许开发者使用 HTML、CSS 和 JavaScript 等 Web 技术来开发桌面应用。

首先,Electron 并非将 JavaScript 与原生代码对立,而是提供了一种将 Web 应用与原生代码(如 C++、Objective-C 或 Rust)相结合的能力。许多应用(如 1Password)通过在 Electron 中使用大量原生代码,实现了高性能和功能丰富的用户体验。Electron 的核心优势在于开发者可以根据需求灵活地选择 Web 技术与原生代码的结合方式。

其次,关于 Web 应用与原生应用的优劣之争,Felix 强调,Web 技术已经证明了其在构建用户界面方面的强大能力。许多成功的应用(如 NASA 的 Mission Control、Bloomberg Terminal、SpaceX 的 Dragon 2 空间舱等)都采用了 Web 技术。虽然原生应用在某些情况下具有优势,但 Web 应用的灵活性、跨平台能力和快速迭代能力使其在许多场景中更具竞争力。

第三,Electron 使用的 Chromium 渲染引擎并不逊色于操作系统内置的 WebView。尽管操作系统内置的 WebView 在某些情况下可能看起来更轻量级,但 Chromium 作为浏览器技术的前沿,提供了更强大的性能和功能支持。例如,Slack 最初使用 macOS 的内置 WebView,但最终转向 Electron,因为其性能和用户体验更好。

此外,Electron 的应用体积较大(通常在 100MB 到 300MB 之间)也常被提及。然而,Felix 指出,在现代网络环境下,用户对应用体积的关注度远低于其他因素,如功能和性能。例如,用户在观看 Netflix 或更新大型游戏时,对数据量的关注度远高于桌面应用的体积。

Felix 强调,Electron 的存在并非为了与其他框架竞争,而是为了填补一个空白——帮助开发者构建他们想要的桌面应用。如果有人能够提供一个更好的解决方案来满足这一需求,Electron 的维护者们会欢迎并感谢这样的努力。

#Electron #JavaScript

https://felixrieseberg.com/things-people-get-wrong-about-electron/
👍1
Anthony Fu 探讨了 JavaScript 生态系统从 CommonJS(CJS)向 ECMAScript 模块(ESM)过渡的趋势和现状,并分享了他对 ESM 采用的思考与建议。

Anthony Fu 回顾了自己在 2022 年关于同时发布 ESM 和 CJS 格式的观点,当时他认为生态系统尚未准备好完全转向 ESM。然而,随着时间推移,工具和生态系统的发展促使他逐渐倾向于支持 ESM-only。他指出,自 2015 年 ESM 首次引入以来,经过十年发展,现代工具和库已越来越多地采用 ESM 作为主要模块格式。根据 WOOORM 的统计,2021 年 npm 上发布 ESM 格式的包占比为 7.8%,到 2024 年底已增长至 25.8%,尽管仍有大量包使用 CJS,但趋势明显向 ESM 倾斜。

他首先讨论了 “工具准备就绪”,指出现代前端构建工具如 Vite 的兴起,以及基于 Vite 的框架(如 Nuxt、SvelteKit、Astro 等)都将 ESM 作为一等公民。此外,测试库 Vitest、CLI 工具 tsx 和 jiti 等也为 ESM 的开发和运行提供了无缝体验。ESLint 也在 v9.0 中引入了对 ESM 的原生支持。

Anthony Fu 接着讨论了 “自顶向下与自底向上” 的 ESM 推广方式。2021 年,Sindresorhus 开始将其所有包迁移到 ESM-only,尽管这一过程并不完全顺利,但为生态系统带来了许多高质量的 ESM 包。他认为,对于 ESM 的平滑采用,自顶向下的方式更为有效,即通过高层框架和工具的支持,降低使用 ESM-only 包的障碍。

他还提到 Node.js 中 “require() ESM 模块的能力” 是一个重要的里程碑,该功能允许 ESM-only 的包被 CJS 代码库以最小的修改使用。这一功能已在 Node.js v22 中正式启用,并计划回溯至 v20,使得 ESM 的采用变得更加灵活。

Anthony Fu 的文章的第二部分讨论了 “双格式的困境”。尽管双格式(CJS/ESM)的包在过渡期间很有帮助,但它们也带来了诸多挑战。首先,CJS 和 ESM 是两种不同的模块系统,存在互操作性问题。例如,CJS 使用单一的 module.exports 对象,而 ESM 支持默认和命名导出,这在代码转换时容易出错。其次,双格式的依赖解析过程复杂,可能导致版本冲突和重复依赖。此外,双格式的包大小会加倍,导致 node_modules 膨胀,增加项目开销。

在第三部分中,Anthony Fu 探讨了 “何时应转向 ESM-only”。他建议所有新包应直接采用 ESM-only,因为不存在遗留依赖问题,且现代开发环境已支持 ESM。对于主要面向浏览器的包,ESM-only 更具优势,因为 ESM 在静态分析和树摇优化方面表现出色,能够生成更小、更高效的代码包。对于独立的 CLI 工具,采用 ESM 有助于推动整个生态系统向 ESM 转型。如果包的目标是支持最新版的 Node.js,那么在当前 require(ESM) 支持的情况下,转向 ESM-only 是一个不错的选择。此外,他还强调了了解用户需求的重要性,例如 ESLint v9 的新配置系统支持 ESM,因此 ESLint 插件可以安全地采用 ESM-only。

在第四部分中,Anthony Fu 介绍了 “我们目前的进展”。他开发了一个名为 Node Modules Inspector 的工具,用于分析项目依赖的 ESM 采用情况,并帮助识别迁移过程中可能出现的问题。该工具提供了依赖关系图和 ESM 采用情况报告等功能,尽管仍处于早期阶段,但有望成为包作者和维护者跟踪 ESM 进程的有力工具。

Anthony Fu 表示将逐步将其维护的包迁移到 ESM-only,并继续完善 Node Modules Inspector 工具,以提供更有用的见解。他期待一个更便携、更具弹性和优化的 JavaScript/TypeScript 生态系统,并鼓励读者在评论区交流想法和问题。

#前端 #JavaScript

https://antfu.me/posts/move-on-to-esm-only
如何优雅的封装 Try/Catch。

function tryCatch(fn, ...args) {
try {
const result = fn.apply(null, args);

if (result.then) {
return new Promise(resolve => {
result
.then(v => resolve([undefined, v]))
.catch(e => resolve([e, undefined]))
});
}

return [undefined, result];
} catch (e) {
return [e, undefined];
}
}


#JavaScript

https://nalanj.dev/posts/safe-assignment/
Yjs 是一个用于构建实时协作应用的库,例如多人协作的 Figma 或 Google Docs。其核心是 CRDT,一种能够在多台计算机上存储状态并自动合并冲突的数据结构,且无需依赖集中式服务器。Yjs 主要包含两部分:文档(Documents) 和 提供者(Providers)。

文档(Documents) 是 Yjs 中存储状态的部分,可以将任何可序列化为 JSON 的数据存储其中。文档能够无缝合并来自其他客户端的更新。而 提供者(Providers) 则负责将文档的变化同步到其他地方,例如通过 WebSocket 在客户端之间传输文档,或者将文档同步到浏览器的本地存储以支持离线访问。

#JavaScript #Tools

https://learn.yjs.dev/lessons/01-introduction/
JavaScript Temporal 是一个全新的日期和时间处理对象,为了取代已有近 30 年历史的 JavaScript Date 对象。Temporal 的出现将极大地简化和现代化 JavaScript 中的日期和时间处理,为依赖于调度、国际化或时间敏感数据的应用程序提供高效、精确且一致的日期、时间、时区和日历支持。

#JavaScript #新特性

https://developer.mozilla.org/en-US/blog/javascript-temporal-is-coming/
在 240 个浏览器标签页中运行乒乓球游戏,挺会玩的,可以直接看视频。

https://eieio.games/images/running-pong-in-240-browser-tabs/gameplay.mp4

#JavaScript #创意

文章链接:

https://eieio.games/blog/running-pong-in-240-browser-tabs/
JavaScript 开发者在 2025 年应该了解的一些重要特性

1. Iterator helpers

在处理大型数组时,传统的链式数组操作(如 arr.slice(10, 20).filter(el => el < 10).map(el => el + 5) )会创建多个临时数组,导致性能低下。

JavaScript 引入了迭代器方法,这些方法不会创建临时数组,而是通过迭代器逐个处理数据,从而节省内存。

常用迭代器方法:

Iterator.prototype.drop():跳过迭代器开头的指定数量元素。
Iterator.prototype.take():从迭代器开头取指定数量的元素。
Iterator.prototype.some() 和 Iterator.prototype.every():分别用于测试迭代器中是否有元素满足条件或所有元素是否满足条件。
Iterator.prototype.filter()、Iterator.prototype.find()、Iterator.prototype.flatMap():分别用于过滤、查找和展平迭代器中的值。
Iterator.prototype.forEach()、Iterator.prototype.map()、Iterator.prototype.reduce():分别用于遍历、映射和归并迭代器中的值。
Iterator.prototype.toArray():将迭代器中的值转换为数组。

2. Array at() method

Array.prototype.at() 是一种替代方式,用于访问数组中的第 n 个元素。它支持负索引,从数组末尾开始计数。

[10, 20, 30].at(-1) 返回 30。

3. Promise.withResolvers()

Promise.withResolvers() 可以直接返回一个包含 promise、resolve 和 reject 的对象,简化了代码。


const { promise, resolve, reject } = Promise.withResolvers();


4. String.prototype.replace() / String.prototype.replaceAll() callback(字符串替换的回调函数)

String.prototype.replace()String.prototype.replaceAll() 的第二个参数可以是一个回调函数,而不仅仅是字符串。


let counter = 0;
console.log("NUMBER, NUMBER, NUMBER".replaceAll("NUMBER", (match) => match + "=" + (++counter)));
// 输出:NUMBER=1, NUMBER=2, NUMBER=3


5. Swapping variables(交换变量)


let a = 1, b = 2;
[a, b] = [b, a];


6. structuredClone()(结构化克隆)

开发者通常使用 JSON.stringify()JSON.parse() 来深拷贝对象,但这种方法存在以下问题:

- 不支持某些值(如 NaN、undefined 和 bigint)。
- 无法处理包含循环引用的对象。
- 对于大型对象,效率低下且浪费内存。

structuredClone() 是浏览器提供的 API,可以更高效地深拷贝对象,并且自动处理循环引用。


const obj = {};
obj.selfReference = obj;
const clonedObj = structuredClone(obj);
console.log(obj === clonedObj); // false
console.log(clonedObj.selfReference === clonedObj); // true



7. Tagged templates

标记模板允许通过函数解析模板字符串。第一个参数是一个字符串数组,其余参数是表达式的值。可以在插值值(或整个字符串)上执行自动转换。

8. WeakMap / WeakSet

WeakMapWeakSet 是类似于 Map 和 Set 的数据结构,但它们的键必须是对象,且不支持迭代器。当键的所有引用丢失时,键和可能的值可以被垃圾回收器回收,从而避免内存泄漏。

9. Set operations

JavaScript 现在支持对 Set 对象进行布尔运算。

Set.prototype.difference():返回一个新集合,包含当前集合中但不在给定集合中的元素。
Set.prototype.intersection():返回一个新集合,包含当前集合和给定集合的交集。
Set.prototype.union():返回一个新集合,包含当前集合和给定集合的并集。
Set.prototype.symmetricDifference():返回一个新集合,包含当前集合和给定集合的对称差集。
Set.prototype.isDisjointFrom():判断当前集合与给定集合是否没有交集。
Set.prototype.isSubsetOf():判断当前集合是否是给定集合的子集。
Set.prototype.isSupersetOf():判断当前集合是否是给定集合的超集。

#JavaScript #新特性

https://waspdev.com/articles/2025-04-06/features-that-every-js-developer-must-know-in-2025
Lazy Brush 是一款由 dulnan 开发的 JavaScript 库,能够实现通过鼠标、手指或其他指向设备在画布上绘制平滑曲线和直线。

#Tools #JavaScript

https://lazybrush.dulnan.net/