JavaScript
2025-07-01
主要变化
异步处理改进
- 新增 Promise.try () 方法 :用于统一封装同步返回值或抛错的函数,相比以往的 Promise.resolve ().then (fn) 或 new Promise (resolve => resolve (fn ())),Promise.try (fn) 更简洁高效。例如,对于一个可能会抛出异常的函数,使用 Promise.try () 可以更方便地进行错误处理。
- 可立即抛出同步异常 :使用 Promise.try () 时,若函数同步抛出异常,能够立即捕获并处理,避免了使用 Promise.resolve ().then (fn) 时引入的微任务延迟,提高了错误可见性与调试效率,适用于封装第三方同步 API,使其具备统一的异步处理能力。
集合操作增强
- 新增 Set 实例方法 :为 Set 实例新增了七个方法,包括集合运算方法 intersection ()(交集)、union ()(并集)、difference ()(差集)、symmetricDifference ()(对称差集),以及集合关系方法 isSubsetOf ()(是否为子集)、isSupersetOf ()(是否为超集)、isDisjointFrom ()(是否无交集),使集合操作更加方便快捷,符合数学上的集合运算逻辑。
迭代器功能扩展
- 新增同步迭代器辅助函数 :为所有同步迭代器添加了一系列辅助方法,如 .map (fn)、.filter (fn)、.flatMap (fn)、.some (fn)、.every (fn)、.find (fn)、.reduce (fn, init)、.forEach (fn)、.drop (n)、.take (n)、.toArray () 等。这些方法支持链式调用,可用于链式处理可迭代对象的数据,实现惰性求值,避免创建多个中间数组,提升内存效率,特别适合处理大型或无限可迭代数据,如生成器、流数据等。
正则表达式增强
- 新增 RegExp.escape () 方法 :可将字符串中的正则元字符转义,使其能够安全地嵌入正则表达式中,避免动态生成正则表达式时出现语法错误,防止正则注入漏洞,替代手动维护的转义函数。
- 正则表达式内联标志 :允许在正则表达式内部使用内联语法 (?flags:...) 或 (?flags1-flags2:...) 以局部开启或关闭某些标志位,如 i、m、s 等。例如,在正则 /^x (?i:HELLO) x$/ 中,整个表达式外部没有 i 标志,而只对子串 HELLO 应用忽略大小写,避免了正则拆分与多轮匹配逻辑。
- 重复命名捕获组 :允许在正则表达式的不同分支中使用相同的命名捕获组名称,只要这些同名组不可能同时匹配。这便于对形式不同但结构类似的文本进行统一处理,如解析多种日期格式、键值对格式等,可简化后续处理逻辑,避免代码冗余。
模块系统优化
新增导入属性,允许在 import 语句中指定附加信息,以指定如何加载模块,主要用于引入非 JavaScript 资源,如 JSON 文件或 CSS 模块。静态导入时,可在路径后加上 with 选项;动态导入时,将其放在第二个参数的 with 字段中,使用起来更加方便简洁,可直接像引用 JS 模块一样使用 JSON 数据等。
数值表示扩展
提供对 16 位浮点数的原生支持,包括 Float16Array、DataView.prototype.getFloat16 ()/setFloat16 () 以及 Math.f16round (number)。这在 WebGPU / WebGL 中可节省带宽与内存,在深度学习中便于传递模型参数,也可用于模拟硬件精度限制。
其他特性
- 后置检查的声明式控制流 :引入了 checked { } 块和 assert 关键字,在 checked 块中的操作会在执行后立即检查是否出界等,assert 用于断言,若条件不成立会直接抛出异常,为开发者提供了更灵活的错误检查方式。
- ArrayBuffer 的构造共享数组 :新增了 ArrayBuffer 构造函数的 shared 构造标志,可创建一个共享的 ArrayBuffer,其视图成为共享数组,所有代理都具有相同的内存视图,允许多个 JavaScript 工作线程之间共享和传递 ArrayBuffer,提高了数据共享和传递的效率。ÎÍ
历史变革
ECMAScript(简称 ES)作为 JavaScript 的标准化规范,自 1997 年发布首个版本以来,已经历了多次重要更新。下面为你介绍 ECMAScript 各主要版本及其核心变化:
- ES1(1997 年):这是 ECMAScript 的首个版本,它的制定参考了 JavaScript 1.1,是一个基础版本,为后续的发展奠定了基础。
- ES2(1998 年):此版本主要是为了使规范与 ISO/IEC 16262 国际标准保持一致,并没有对语言功能进行实质性的扩展。
- ES3(1999 年):这是一个具有重要意义的版本,引入了众多核心特性,包括正则表达式、try/catch 异常处理、do-while 循环以及更完善的字符串处理功能等,极大地丰富了 ECMAScript 的功能。
- ES4(未发布):该版本计划进行大幅更新,但由于各方在设计理念上存在较大分歧,最终未能正式发布。
- ES5(2009 年):引入了严格模式(strict mode),对 JSON 对象进行了标准化,还新增了数组方法(如 forEach、map、filter)和
Object.defineProperty 等特性,提升了代码的规范性和可维护性。
- ES5.1(2011 年):这是一个修正版本,主要是对 ES5 的规范进行了澄清和完善,使其更加严谨。
- ES6(ES2015):带来了众多革命性的特性,如箭头函数、let/const 块级作用域、Promise 异步编程、class 类和继承、模块系统(import/export)以及解构赋值等,使 ECMAScript 的语法更加现代化。
- ES2016(ES7):新增了数组 includes 方法和指数运算符(**),进一步扩展了 ECMAScript 的功能。
- ES2017(ES8):引入了 async/await 异步编程模式、
Object.values/entries、字符串填充(padStart/padEnd)以及共享内存和原子操作等特性,提升了异步编程的便利性。
- ES2018(ES9):支持异步迭代器、Rest/Spread 属性、
Promise.finally 以及正则表达式的一些增强功能,如后行断言等,增强了语言的灵活性。
- ES2019(ES10):新增了
Array.flat/flatten、Object.fromEntries、String.trimStart/End、可选的 catch 绑定以及 Symbol.description 等特性,简化了代码编写。
- ES2020(ES11):引入了 BigInt 数据类型、空值合并运算符(??)、可选链操作符(?.)、
Promise.allSettled、globalThis 以及模块动态导入等功能,提升了语言的表达能力。
- ES2021(ES12):包含逻辑赋值运算符(&&=、||=、??=)、数字分隔符(_)、Promise.any 和 WeakRefs 等特性,进一步完善了语言功能。
- ES2022(ES13):新增了类字段声明、静态类字段和私有方法、Top-level await 以及 Error Cause 等特性,使类的定义更加简洁和强大。
- ES2023(ES14):引入了 Array find from last、Hashbang 语法、WeakMap 和 WeakSet 的新方法以及 Symbol.dispose 和 Disposable Objects 等特性,增强了语言的实用性。
- ES2024(ES15):预计会增加 Decorators(装饰器)、Record & Tuple(不可变数据结构)等特性,目前还在不断发展和完善中。
从 ES6 开始,ECMAScript 采用了每年一次的发布周期,版本号以年份命名,这种方式使得新特性能够更快地进入标准,满足开发者的需求。
| ES 版本 |
Node.js 最小支持版本 |
Chrome 最小支持版本 |
Firefox 最小支持版本 |
| ES6 (ES2015) |
v4.0.0 (部分) |
49 |
45 |
|
v6.0.0 (接近完整) |
|
|
| ES2016 (ES7) |
v7.0.0 |
51 |
52 |
| ES2017 (ES8) |
v8.0.0 |
58 |
52 |
| ES2018 (ES9) |
v10.0.0 |
63 |
57 |
| ES2019 (ES10) |
v12.0.0 |
73 |
67 |
| ES2020 (ES11) |
v14.0.0 |
80 |
74 |
| ES2021 (ES12) |
v16.0.0 |
91 |
90 |
| ES2022 (ES13) |
v18.0.0 |
96 |
97 |
| ES2023 (ES14) |
v20.0.0 |
110 |
111 |
| ES2024 (ES15) |
v21.0.0 (部分) |
119 (部分) |
120 (部分) |
在生产环境中使用新特性时,建议搭配 Babel 等转译工具或 TypeScript 以确保兼容性。
参考资料与拓展阅读
JavaScript
2024-06-01
WebDev JavaScript
2023-04-21
看了 How to split JavaScript strings into sentences, words or graphemes with "Intl.Segmenter" 了解到,现在 Web 已经支持分词了:
const text = `我爱北京天安门,天安门上太阳升。伟大领袖毛主席,指引我们向前进。`;
const granularities = ["sentence", "word", "grapheme"];
granularities.forEach(function (granularity) {
// console.log([granularity, index, self])
let segmenter = new Intl.Segmenter("zh", { granularity: granularity });
let seg = segmenter.segment(text);
// console.log(seg) // Segments{}
let result = Array.from(seg, (s) => s.segment);
console.log(result);
});
// ['我爱北京天安门,天安门上太阳升。', '伟大领袖毛主席,指引我们向前进。']
// ['我', '爱', '北京', '天安门', ',', '天安门', '上', '太阳', '升', '。', '伟大', '领袖', '毛主席', ',', '指引', '我们', '向', '前进', '。']
// ['我', '爱', '北', '京', '天', '安', '门', ',', '天', '安', '门', '上', '太', '阳', '升', '。', '伟', '大', '领', '袖', '毛', '主', '席', ',', '指', '引', '我', '们', '向', '前', '进', '。']
WebDev JavaScript Ajax
2022-08-02
在很长一段时间里,浏览器发送 Ajax 请求主要依赖 XMLHttpRequest,也就是大家熟悉的 XHR。
但随着 JavaScript 生态的发展,XHR 的一些问题越来越明显:
- 代码太啰嗦
- 回调层级复杂
- 错误处理麻烦
- 可读性较差
于是浏览器推出了更现代、更简洁的网络请求方案 Fetch API。
XMLHttpRequest 的问题
先看一个传统 Ajax 请求:
var xhr = new XMLHttpRequest();
xhr.open("GET", "/api/user", true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
console.log(xhr.responseText);
}
}
};
xhr.send();
虽然功能没问题,但存在几个明显缺点:
- 状态判断复杂:必须反复判断 readyState 和 status,初学者经常搞混。
- 回调代码较多:所有逻辑都写在 onreadystatechange 里面,代码一多就会非常乱。
- 服务器返回 JSON 时需要手动转换。
JSON.parse(xhr.responseText)
Fetch 的基本写法
主要是基于 Promise 实现,可以链式调用,不用一层又一层地回调,更符合现代 JavaScript 风格。
注意:这是较新的语法,在某些老版本浏览器上可能支持程度不好,如果需要兼容,可以使用 XHR 或者 Axios、jQuery Ajax 等库。
fetch("/api/user")
.then(function (response) { // Response 对象
return response.json();
})
.then(function (data) {
console.log(data);
})
.catch(function (error) { // 异常处理
console.log(error);
});
相比 XMLHttpRequest:
Fetch 也支持提交数据:
fetch("/api/user", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
name: "Tom",
age: 18
})
})
.then(function (response) {
return response.json();
})
.then(function (data) {
console.log(data);
});
参考资料与拓展阅读
JavaScript WebFrontend 字符编码 乱码问题 Unicode
2019-06-20
function base64EncodeUnicode(str) {
// First we escape the string using encodeURIComponent to get the UTF-8 encoding of the characters,
// then we convert the percent encodings into raw bytes, and finally feed it to btoa() function.
utf8Bytes = encodeURIComponent(str).replace(
/%([0-9A-F]{2})/g,
function (match, p1) {
return String.fromCharCode("0x" + p1);
},
);
return btoa(utf8Bytes);
}
x.decode('utf-8').encode('raw_unicode_escape').decode('ascii')[2:].strip('0')
JavaScript NodeJS
2017-02-12
"ni wo ta".split(" ");
// [ 'ni', 'wo', 'ta' ]
"ni wo ta".split(" ", 1);
// [ 'ni' ]
"ni wo ta".split(" ", 2);
// [ 'ni', 'wo' ]
"ni wo ta".split(" ", 3);
// [ 'ni', 'wo', 'ta' ]
"ni wo ta".split(" ", 4);
// [ 'ni', 'wo', 'ta' ]
"ni wo ta".split(":");
// [ 'ni wo ta' ]
"ni wo ta".split(":", 1);
// [ 'ni wo ta' ]
"ni wo ta".split(":", 2);
// [ 'ni wo ta' ]
如果要一刀将字符串切两半:
var line = "a : b : c";
var part1 = line.split(":", 1)[0];
if (a !== line) {
var a = part1.trim();
var b = line.substr(part1.length + 1).trim();
console.log([a, b]);
}
var line = "a : b : c";
var index = line.indexOf(":");
if (index != -1) {
var a = line.substr(0, index).trim();
var b = line.substr(index + 1).trim();
console.log([a, b]);
}
参考资料与拓展阅读
WebFrontend JavaScript jQuery
2015-11-09
jQuery 插件的开发包括两种:
一种是类级别的插件开发,即给 jQuery 添加新的全局函数,相当于给 jQuery 类本身添加方法(jQuery 的全局函数就是属于 jQuery 命名空间的函数)。
另一种是对象级别的插件开发,即给 jQuery 对象添加方法。下面就两种函数的开发做详细的说明。
Web JavaScript
2015-01-30
WebFrontend JavaScript Ajax
2011-05-12
Ajax 是一种让网页“不用刷新页面,也能和服务器通信”的技术。
比如:
- 搜索框自动提示
- 点赞后数字立即变化
- 提交表单后局部刷新
这些功能背后通常都用了 Ajax。
什么是 Ajax?
Ajax 全称 Asynchronous JavaScript and XML,意思是 JS 异步请求服务器数据,虽然名字里有 XML,但实际开发里更多使用 JSON。
传统网页的工作流程:
- 点击按钮
- 浏览器请求服务器
- 服务器返回完整页面
- 浏览器整体刷新
问题:
Ajax 出现后:
- 页面无需整体刷新
- 后台获取数据
- 只更新局部区域
- 交互体验更流畅
Ajax 的实现方式
- 创建 XMLHttpRequest 对象
- 配置请求
- 发送请求
- 等待服务器响应
- 更新页面内容
GET 获取数据
function loadUser() {
// 创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
// 配置请求
xhr.open("GET", "/api/user", true);
// 监听状态变化
xhr.onreadystatechange = function () {
// 请求完成
if (xhr.readyState == 4) {
// 请求成功
if (xhr.status == 200) {
// 输出服务器返回的数据
console.log(xhr.responseText);
} else {
console.log("请求失败");
}
}
};
// 发送请求
xhr.send();
}
POST 提交数据
function submitData() {
var xhr = new XMLHttpRequest();
xhr.open("POST", "/api/user", true);
// 设置请求头
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log("提交成功");
}
};
// 提交数据
xhr.send("name=Tom&age=18");
}
IE 浏览器兼容
IE6/IE7 需要使用:
ActiveXObject;