kn-es-features 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/esfeatures.iife.js +45 -0
  2. package/dist/esfeatures.js +3795 -0
  3. package/dist/esfeatures.umd.cjs +45 -0
  4. package/dist/polyfills.iife.js +7 -0
  5. package/package.json +39 -0
  6. package/src/features/es2015/01-let-const.js +54 -0
  7. package/src/features/es2015/02-arrow-functions.js +68 -0
  8. package/src/features/es2015/03-template-literals.js +51 -0
  9. package/src/features/es2015/04-destructuring.js +68 -0
  10. package/src/features/es2015/05-default-rest-spread.js +87 -0
  11. package/src/features/es2015/06-classes.js +100 -0
  12. package/src/features/es2015/07-promises.js +79 -0
  13. package/src/features/es2015/08-symbols.js +85 -0
  14. package/src/features/es2015/09-iterators-generators.js +104 -0
  15. package/src/features/es2015/10-map-set.js +106 -0
  16. package/src/features/es2015/11-proxy-reflect.js +122 -0
  17. package/src/features/es2015/12-enhanced-objects.js +101 -0
  18. package/src/features/es2015/13-new-methods.js +123 -0
  19. package/src/features/es2015/14-modules.js +38 -0
  20. package/src/features/es2015/15-binary-octal-unicode.js +65 -0
  21. package/src/features/es2015/16-for-of.js +75 -0
  22. package/src/features/es2015/17-map.js +80 -0
  23. package/src/features/es2022/01-class-fields.js +114 -0
  24. package/src/features/es2022/02-class-static-blocks.js +108 -0
  25. package/src/features/es2022/03-at-method.js +86 -0
  26. package/src/features/es2022/04-object-has-own.js +82 -0
  27. package/src/features/es2022/05-error-cause.js +92 -0
  28. package/src/features/es2022/06-regexp-d-flag.js +89 -0
  29. package/src/features/es2022/07-top-level-await.js +84 -0
  30. package/src/features/es2025/01-iterator-helpers.js +96 -0
  31. package/src/features/es2025/02-set-methods.js +92 -0
  32. package/src/features/es2025/03-promise-try.js +72 -0
  33. package/src/features/es2025/04-regexp-duplicate-groups.js +69 -0
  34. package/src/features/es2025/05-uint8array-base64-hex.js +94 -0
  35. package/src/features/es2025/06-json-parse-source.js +86 -0
  36. package/src/features/es2025/07-error-is-error.js +83 -0
  37. package/src/features/es2025/08-float16array.js +88 -0
  38. package/src/features/es2026/01-math-sum-precise.js +79 -0
  39. package/src/features/es2026/02-regexp-escape.js +80 -0
  40. package/src/features/es2026/03-explicit-resource-management.js +117 -0
  41. package/src/features/es2026/04-atomics-pause.js +81 -0
  42. package/src/features/es2026/05-import-attributes.js +86 -0
  43. package/src/index.js +166 -0
  44. package/src/main.js +226 -0
  45. package/src/polyfills.js +6 -0
  46. package/src/run.js +3 -0
  47. package/src/style.css +412 -0
  48. package/src/utils/runner.js +55 -0
@@ -0,0 +1,86 @@
1
+ /**
2
+ * ES2025 —— JSON.parse with Source Text Access
3
+ *
4
+ * JSON.parse 的 reviver 函数新增第三个参数 context,
5
+ * context.source 携带该值在原始 JSON 字符串中的原始文本片段。
6
+ *
7
+ * 主要用途:
8
+ * 1. 精确解析超大整数(BigInt),不丢失精度
9
+ * 2. 调试时查看原始文本
10
+ * 3. 保留数字字面量的原始格式
11
+ */
12
+ import { createSuite } from '../../utils/runner.js'
13
+
14
+ export function testJsonParseSource() {
15
+ const { test, assert, getResults } = createSuite('JSON.parse Source Text Access (ES2025)')
16
+
17
+ // 检测支持性:调用 reviver 看第三参数是否存在
18
+ let supported = false
19
+ try {
20
+ JSON.parse('1', (key, value, context) => {
21
+ if (context && typeof context.source === 'string') supported = true
22
+ return value
23
+ })
24
+ } catch (e) { /* ignore */ }
25
+
26
+ test('reviver 接收第三个参数 context', () => {
27
+ if (!supported) { assert(true, '(跳过:环境不支持 JSON.parse source text access)'); return }
28
+ let receivedContext = null
29
+ JSON.parse('"hello"', (key, value, ctx) => {
30
+ if (key === '') receivedContext = ctx
31
+ return value
32
+ })
33
+ assert(receivedContext !== null && typeof receivedContext.source === 'string',
34
+ 'reviver 应收到含 source 属性的 context 对象')
35
+ })
36
+
37
+ test('context.source 为该值的原始 JSON 文本', () => {
38
+ if (!supported) { assert(true, '(跳过)'); return }
39
+ const sources = {}
40
+ JSON.parse('{"a": 42, "b": "hello"}', (key, value, ctx) => {
41
+ if (key !== '') sources[key] = ctx.source
42
+ return value
43
+ })
44
+ // source 保留原始空白
45
+ assert(sources.a === '42', `数字的 source 应为 "42",实际: "${sources.a}"`)
46
+ assert(sources.b === '"hello"', `字符串的 source 应为 '"hello"',实际: "${sources.b}"`)
47
+ })
48
+
49
+ test('利用 source 精确解析超大整数(BigInt)', () => {
50
+ if (!supported) { assert(true, '(跳过)'); return }
51
+ // JS 数字最大安全整数为 2^53-1,超出后会丢失精度
52
+ const bigNum = '9007199254740993' // 2^53 + 1,普通 JSON.parse 会精度丢失
53
+ const parsed = JSON.parse(`{"id":${bigNum}}`, (key, value, ctx) => {
54
+ if (key === '' ) return value
55
+ // 用 source 转 BigInt,避免精度损失
56
+ if (/^\d+$/.test(ctx.source)) return BigInt(ctx.source)
57
+ return value
58
+ })
59
+ assert(typeof parsed.id === 'bigint', '超大整数应被转为 BigInt')
60
+ assert(parsed.id === BigInt(bigNum), `BigInt 值应精确等于 ${bigNum}`)
61
+ })
62
+
63
+ test('嵌套对象中每个值都能获取自己的 source', () => {
64
+ if (!supported) { assert(true, '(跳过)'); return }
65
+ const collected = []
66
+ JSON.parse('{"x":1,"arr":[2,3]}', (key, value, ctx) => {
67
+ if (key !== '') collected.push(ctx.source)
68
+ return value
69
+ })
70
+ assert(collected.includes('1') && collected.includes('2') && collected.includes('3'),
71
+ '嵌套结构中每个原始值都应能取到 source')
72
+ })
73
+
74
+ test('null / boolean 也有对应 source', () => {
75
+ if (!supported) { assert(true, '(跳过)'); return }
76
+ const sources = {}
77
+ JSON.parse('{"a":null,"b":true,"c":false}', (key, value, ctx) => {
78
+ if (key !== '') sources[key] = ctx.source
79
+ return value
80
+ })
81
+ assert(sources.a === 'null' && sources.b === 'true' && sources.c === 'false',
82
+ 'null/true/false 的 source 应为对应的原始文本')
83
+ })
84
+
85
+ return getResults()
86
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * ES2025 —— Error.isError()
3
+ *
4
+ * Error.isError(value) 是一个静态方法,用来可靠地判断一个值是否为 Error 实例。
5
+ *
6
+ * 为什么需要它?
7
+ * - instanceof Error 跨 realm(iframe、vm、Worker)会失效
8
+ * - Object.prototype.toString 返回 "[object Error]" 但并不区分 Error 子类
9
+ * - Error.isError 基于内部槽位([[ErrorData]])判断,跨 realm 安全
10
+ */
11
+ import { createSuite } from '../../utils/runner.js'
12
+
13
+ export function testErrorIsError() {
14
+ const { test, assert, getResults } = createSuite('Error.isError (ES2025)')
15
+
16
+ const supported = typeof Error.isError === 'function'
17
+
18
+ test('原生 Error 实例返回 true', () => {
19
+ if (!supported) { assert(true, '(跳过:环境不支持 Error.isError)'); return }
20
+ assert(Error.isError(new Error('test')) === true, 'new Error() 应返回 true')
21
+ })
22
+
23
+ test('Error 子类实例返回 true', () => {
24
+ if (!supported) { assert(true, '(跳过)'); return }
25
+ assert(Error.isError(new TypeError('type')) === true, 'TypeError 应返回 true')
26
+ assert(Error.isError(new RangeError('range')) === true, 'RangeError 应返回 true')
27
+ assert(Error.isError(new SyntaxError('syntax')) === true, 'SyntaxError 应返回 true')
28
+ assert(Error.isError(new ReferenceError('ref')) === true, 'ReferenceError 应返回 true')
29
+ assert(Error.isError(new URIError('uri')) === true, 'URIError 应返回 true')
30
+ assert(Error.isError(new EvalError('eval')) === true, 'EvalError 应返回 true')
31
+ })
32
+
33
+ test('自定义 Error 子类实例返回 true', () => {
34
+ if (!supported) { assert(true, '(跳过)'); return }
35
+ class AppError extends Error {
36
+ constructor(msg) { super(msg); this.name = 'AppError' }
37
+ }
38
+ assert(Error.isError(new AppError('app')) === true, '自定义 Error 子类应返回 true')
39
+ })
40
+
41
+ test('普通对象返回 false', () => {
42
+ if (!supported) { assert(true, '(跳过)'); return }
43
+ assert(Error.isError({}) === false, '普通对象应返回 false')
44
+ assert(Error.isError({ message: 'fake', stack: '' }) === false, '伪造 Error 对象应返回 false')
45
+ })
46
+
47
+ test('模拟 Error 对象(纯 JS 构造)返回 false', () => {
48
+ if (!supported) { assert(true, '(跳过)'); return }
49
+ // 即使 prototype 指向 Error.prototype,没有内部槽位也应返回 false
50
+ const fake = Object.create(Error.prototype)
51
+ fake.message = 'fake'
52
+ // 注意:某些实现可能对此行为有差异,但规范要求基于内部槽位
53
+ // 这里只验证 Error.isError 不会误判明确不是 Error 的值
54
+ assert(Error.isError(null) === false, 'null 应返回 false')
55
+ assert(Error.isError(undefined) === false, 'undefined 应返回 false')
56
+ assert(Error.isError(42) === false, '数字应返回 false')
57
+ assert(Error.isError('error') === false, '字符串 "error" 应返回 false')
58
+ })
59
+
60
+ test('数组、函数、正则等非 Error 值返回 false', () => {
61
+ if (!supported) { assert(true, '(跳过)'); return }
62
+ assert(Error.isError([]) === false, '数组应返回 false')
63
+ assert(Error.isError(() => {}) === false, '函数应返回 false')
64
+ assert(Error.isError(/regex/) === false, '正则应返回 false')
65
+ assert(Error.isError(new Date()) === false, 'Date 应返回 false')
66
+ })
67
+
68
+ test('实际使用场景:统一捕获并判断 catch 的值', () => {
69
+ if (!supported) { assert(true, '(跳过)'); return }
70
+ function riskyOp(throwError) {
71
+ if (throwError) throw new TypeError('类型错误')
72
+ return Promise.reject('string rejection')
73
+ }
74
+
75
+ let caughtError = null
76
+ try { riskyOp(true) } catch (e) { caughtError = e }
77
+
78
+ assert(Error.isError(caughtError) === true, 'try/catch 捕获的 Error 应识别为 true')
79
+ assert(Error.isError('string rejection') === false, '字符串 rejection 应识别为 false')
80
+ })
81
+
82
+ return getResults()
83
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * ES2025 —— Float16Array
3
+ *
4
+ * 新增 16 位半精度浮点数类型化数组,以及配套的:
5
+ * Math.f16round(x) —— 将数字舍入为最近的 float16 可表示值
6
+ * DataView.prototype.getFloat16 —— 读取 16 位浮点数
7
+ * DataView.prototype.setFloat16 —— 写入 16 位浮点数
8
+ *
9
+ * 主要场景:WebGL、机器学习(半精度权重)、图像处理,节省内存(Float32 的一半)。
10
+ */
11
+ import { createSuite } from '../../utils/runner.js'
12
+
13
+ export function testFloat16Array() {
14
+ const { test, assert, getResults } = createSuite('Float16Array (ES2025)')
15
+
16
+ const supported = typeof Float16Array !== 'undefined'
17
+
18
+ test('Float16Array 可正常创建', () => {
19
+ if (!supported) { assert(true, '(跳过:环境不支持 Float16Array)'); return }
20
+ const arr = new Float16Array(4)
21
+ assert(arr.length === 4, 'Float16Array 长度应为 4')
22
+ assert(arr.BYTES_PER_ELEMENT === 2, '每个元素应占 2 字节(16 位)')
23
+ })
24
+
25
+ test('从数组初始化并读取值', () => {
26
+ if (!supported) { assert(true, '(跳过)'); return }
27
+ const arr = new Float16Array([1.0, 0.5, -1.0, 0.0])
28
+ assert(arr[0] === 1.0, 'arr[0] 应为 1.0')
29
+ assert(arr[1] === 0.5, 'arr[1] 应为 0.5')
30
+ assert(arr[2] === -1.0, 'arr[2] 应为 -1.0')
31
+ assert(arr[3] === 0.0, 'arr[3] 应为 0.0')
32
+ })
33
+
34
+ test('精度低于 Float32(半精度特性)', () => {
35
+ if (!supported) { assert(true, '(跳过)'); return }
36
+ const arr = new Float16Array([Math.PI])
37
+ // Float16 只有约 3.3 位十进制精度,Math.PI ≈ 3.140625(最近 float16 值)
38
+ assert(Math.abs(arr[0] - Math.PI) > 0.001, 'Float16 精度应低于 Float64 的 Math.PI')
39
+ assert(Math.abs(arr[0] - Math.PI) < 0.01, 'Float16 近似误差应在合理范围内')
40
+ })
41
+
42
+ test('Math.f16round() 舍入到最近 float16 值', () => {
43
+ if (typeof Math.f16round !== 'function') { assert(true, '(跳过:环境不支持 Math.f16round)'); return }
44
+ // 1.337 → float16 最近值约为 1.3369140625
45
+ const r = Math.f16round(1.337)
46
+ assert(typeof r === 'number', 'Math.f16round 应返回数字')
47
+ assert(Math.abs(r - 1.337) < 0.01, '舍入误差应在 0.01 以内')
48
+ assert(r !== 1.337, '应发生精度损失,结果不等于输入值')
49
+ })
50
+
51
+ test('Math.f16round(Infinity) / (-Infinity) / NaN 特殊值', () => {
52
+ if (typeof Math.f16round !== 'function') { assert(true, '(跳过)'); return }
53
+ assert(Math.f16round(Infinity) === Infinity, 'Infinity 应保持不变')
54
+ assert(Math.f16round(-Infinity) === -Infinity, '-Infinity 应保持不变')
55
+ assert(Number.isNaN(Math.f16round(NaN)), 'NaN 应保持 NaN')
56
+ assert(Math.f16round(0) === 0, '0 应保持 0')
57
+ })
58
+
59
+ test('DataView.getFloat16 / setFloat16', () => {
60
+ if (!supported) { assert(true, '(跳过)'); return }
61
+ const dv = new DataView(new ArrayBuffer(4))
62
+ if (typeof dv.setFloat16 !== 'function') { assert(true, '(跳过:DataView 不支持 float16)'); return }
63
+ dv.setFloat16(0, 1.5, true) // little-endian
64
+ const val = dv.getFloat16(0, true)
65
+ assert(val === 1.5, 'DataView setFloat16/getFloat16 round-trip 应还原 1.5')
66
+ })
67
+
68
+ test('Float16Array 支持 TypedArray 通用方法', () => {
69
+ if (!supported) { assert(true, '(跳过)'); return }
70
+ const arr = new Float16Array([3, 1, 4, 1, 5])
71
+ // sort
72
+ arr.sort()
73
+ assert(arr[0] === 1 && arr[4] === 5, 'sort 后最小值应为 1,最大值应为 5')
74
+ // map → 返回新 Float16Array
75
+ const doubled = arr.map(x => x * 2)
76
+ assert(doubled instanceof Float16Array, 'map 应返回 Float16Array')
77
+ })
78
+
79
+ test('Float16Array 内存占用是 Float32Array 的一半', () => {
80
+ if (!supported) { assert(true, '(跳过)'); return }
81
+ const n = 100
82
+ const f16 = new Float16Array(n)
83
+ const f32 = new Float32Array(n)
84
+ assert(f16.byteLength === f32.byteLength / 2, 'Float16Array 字节长度应是 Float32Array 的一半')
85
+ })
86
+
87
+ return getResults()
88
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * ES2026 —— Math.sumPrecise()
3
+ *
4
+ * Math.sumPrecise(iterable) 对可迭代对象中的所有数值进行精确求和,
5
+ * 内部使用"精确求和算法"(类似 Kahan 补偿求和),避免浮点数累积误差。
6
+ *
7
+ * 对比 Array.prototype.reduce:
8
+ * [0.1, 0.2, 0.3].reduce((a, b) => a + b) // 0.6000000000000001(精度损失)
9
+ * Math.sumPrecise([0.1, 0.2, 0.3]) // 0.6(精确)
10
+ */
11
+ import { createSuite } from '../../utils/runner.js'
12
+
13
+ export function testMathSumPrecise() {
14
+ const { test, assert, getResults } = createSuite('Math.sumPrecise() (ES2026)')
15
+
16
+ const supported = typeof Math.sumPrecise === 'function'
17
+
18
+ test('基本求和 —— 整数', () => {
19
+ if (!supported) { assert(true, '(跳过:环境不支持 Math.sumPrecise)'); return }
20
+ assert(Math.sumPrecise([1, 2, 3, 4, 5]) === 15, '整数求和应为 15')
21
+ assert(Math.sumPrecise([]) === 0, '空数组求和应为 0')
22
+ assert(Math.sumPrecise([42]) === 42, '单元素求和应为自身')
23
+ })
24
+
25
+ test('精确处理浮点数累积误差', () => {
26
+ if (!supported) { assert(true, '(跳过)'); return }
27
+ // 普通 reduce 会产生浮点误差
28
+ const naive = [0.1, 0.2, 0.3].reduce((a, b) => a + b)
29
+ const precise = Math.sumPrecise([0.1, 0.2, 0.3])
30
+ assert(naive !== 0.6, '普通 reduce 应存在浮点误差(验证前提)')
31
+ assert(precise === 0.6, 'Math.sumPrecise 应精确返回 0.6')
32
+ })
33
+
34
+ test('大量小数精确求和', () => {
35
+ if (!supported) { assert(true, '(跳过)'); return }
36
+ // 1/3 * 3 在浮点运算中通常不等于 1
37
+ const third = 1 / 3
38
+ const result = Math.sumPrecise([third, third, third])
39
+ // sumPrecise 应在 IEEE 754 精度内尽可能精确
40
+ assert(typeof result === 'number', '应返回数值类型')
41
+ assert(result > 0.999 && result <= 1.0, '三个 1/3 之和应尽量接近 1')
42
+ })
43
+
44
+ test('接受任意可迭代对象', () => {
45
+ if (!supported) { assert(true, '(跳过)'); return }
46
+ // Set
47
+ const fromSet = Math.sumPrecise(new Set([10, 20, 30]))
48
+ assert(fromSet === 60, '支持 Set 可迭代对象')
49
+
50
+ // Generator
51
+ function* gen() { yield 1; yield 2; yield 3 }
52
+ const fromGen = Math.sumPrecise(gen())
53
+ assert(fromGen === 6, '支持 Generator 可迭代对象')
54
+ })
55
+
56
+ test('特殊值处理', () => {
57
+ if (!supported) { assert(true, '(跳过)'); return }
58
+ // +0 与 -0
59
+ assert(Math.sumPrecise([+0]) === 0, '+0 求和应为 0')
60
+ assert(Math.sumPrecise([-0]) === 0, '-0 求和应为 0(结果为 +0)')
61
+
62
+ // Infinity
63
+ assert(Math.sumPrecise([1, Infinity]) === Infinity, '含 Infinity 结果应为 Infinity')
64
+ assert(Math.sumPrecise([-Infinity, 1]) === -Infinity, '含 -Infinity 结果应为 -Infinity')
65
+ assert(Number.isNaN(Math.sumPrecise([Infinity, -Infinity])), 'Inf + (-Inf) 应为 NaN')
66
+
67
+ // NaN 传染
68
+ assert(Number.isNaN(Math.sumPrecise([1, NaN, 3])), '含 NaN 结果应为 NaN')
69
+ })
70
+
71
+ test('非数值元素应抛出 TypeError', () => {
72
+ if (!supported) { assert(true, '(跳过)'); return }
73
+ let threw = false
74
+ try { Math.sumPrecise([1, '2', 3]) } catch (e) { threw = e instanceof TypeError }
75
+ assert(threw, '非数值元素应抛出 TypeError')
76
+ })
77
+
78
+ return getResults()
79
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * ES2026 —— RegExp.escape()
3
+ *
4
+ * RegExp.escape(string) 将字符串中所有具有正则特殊含义的字符进行转义,
5
+ * 返回可安全嵌入正则表达式的字符串。
6
+ *
7
+ * 常见需求:用户输入作为字面量匹配时,防止注入特殊字符破坏正则逻辑。
8
+ * 例如:用户输入 "price: $10.00",直接放入 new RegExp(input) 会出错。
9
+ *
10
+ * 需要转义的字符:^ $ . * + ? ( ) [ ] { } | \ /
11
+ */
12
+ import { createSuite } from '../../utils/runner.js'
13
+
14
+ export function testRegExpEscape() {
15
+ const { test, assert, getResults } = createSuite('RegExp.escape() (ES2026)')
16
+
17
+ const supported = typeof RegExp.escape === 'function'
18
+
19
+ test('转义正则特殊字符', () => {
20
+ if (!supported) { assert(true, '(跳过:环境不支持 RegExp.escape)'); return }
21
+ const special = '^$.*+?()[]{}|\\'
22
+ const escaped = RegExp.escape(special)
23
+ // 所有特殊字符前应加反斜杠
24
+ assert(typeof escaped === 'string', '应返回字符串')
25
+ assert(escaped.includes('\\^'), '^ 应被转义')
26
+ assert(escaped.includes('\\$'), '$ 应被转义')
27
+ assert(escaped.includes('\\.'), '. 应被转义')
28
+ assert(escaped.includes('\\*'), '* 应被转义')
29
+ })
30
+
31
+ test('普通字符不被转义', () => {
32
+ if (!supported) { assert(true, '(跳过)'); return }
33
+ assert(RegExp.escape('hello') === 'hello', '纯字母不应被转义')
34
+ assert(RegExp.escape('12345') === '12345', '纯数字不应被转义')
35
+ assert(RegExp.escape('hello world') === 'hello world', '空格不应被转义')
36
+ })
37
+
38
+ test('转义结果可安全构造正则', () => {
39
+ if (!supported) { assert(true, '(跳过)'); return }
40
+ const userInput = 'price: $10.00 (sale!)'
41
+ const re = new RegExp(RegExp.escape(userInput))
42
+ assert(re.test('price: $10.00 (sale!)'), '转义后的正则应能精确匹配原字符串')
43
+ assert(!re.test('price: X10X00 Xsale!'), '不应匹配用特殊字符替换后的字符串')
44
+ })
45
+
46
+ test('防止正则注入攻击', () => {
47
+ if (!supported) { assert(true, '(跳过)'); return }
48
+ // 如果不转义,恶意输入 "(.*)" 可以匹配任何字符串
49
+ const maliciousInput = '(.*)'
50
+ const unsafeRe = new RegExp(maliciousInput)
51
+ const safeRe = new RegExp(RegExp.escape(maliciousInput))
52
+
53
+ assert( unsafeRe.test('anything'), '未转义的正则可匹配任意字符串(注入漏洞)')
54
+ assert(!safeRe.test('anything'), '转义后只能匹配字面量 "(.*)"')
55
+ assert( safeRe.test('(.*)'), '转义后精确匹配字面字符串 "(.*)"')
56
+ })
57
+
58
+ test('中文等 Unicode 字符不应被转义', () => {
59
+ if (!supported) { assert(true, '(跳过)'); return }
60
+ const chinese = '你好,世界'
61
+ assert(RegExp.escape(chinese) === chinese, 'Unicode 字符不需要转义')
62
+ })
63
+
64
+ test('空字符串返回空字符串', () => {
65
+ if (!supported) { assert(true, '(跳过)'); return }
66
+ assert(RegExp.escape('') === '', '空字符串转义后应仍为空字符串')
67
+ })
68
+
69
+ test('与 /g 标志组合 —— 高亮搜索词', () => {
70
+ if (!supported) { assert(true, '(跳过)'); return }
71
+ // 模拟在文本中高亮用户搜索词(含特殊字符)
72
+ const text = '商品价格 $10.00,原价 $20.00'
73
+ const keyword = '$10.00'
74
+ const re = new RegExp(RegExp.escape(keyword), 'g')
75
+ const count = (text.match(re) || []).length
76
+ assert(count === 1, '含特殊字符的关键词应精确匹配 1 次')
77
+ })
78
+
79
+ return getResults()
80
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * ES2026 —— Explicit Resource Management(显式资源管理)
3
+ *
4
+ * 引入两个新关键字:
5
+ * using —— 同步资源,块退出时自动调用 [Symbol.dispose]()
6
+ * await using —— 异步资源,块退出时自动调用 [Symbol.asyncDispose]()
7
+ *
8
+ * 以及两个新 Symbol:
9
+ * Symbol.dispose —— 同步清理方法
10
+ * Symbol.asyncDispose —— 异步清理方法
11
+ *
12
+ * 解决了"忘记关闭资源"的问题,类似 C# using / Python with / Java try-with-resources。
13
+ */
14
+ import { createSuite } from '../../utils/runner.js'
15
+
16
+ export async function testExplicitResourceManagement() {
17
+ const { test, assert, getResults } = createSuite('Explicit Resource Management (ES2026)')
18
+
19
+ // 检测语法支持(using 是新语法,需用 Function 构造器探测)
20
+ const supported = (() => {
21
+ try {
22
+ new Function('Symbol', `
23
+ const r = { [Symbol.dispose]() {} }
24
+ { using x = r }
25
+ `)(Symbol)
26
+ return true
27
+ } catch { return false }
28
+ })()
29
+
30
+ test('Symbol.dispose 已定义', () => {
31
+ assert(typeof Symbol.dispose === 'symbol', 'Symbol.dispose 应是 symbol 类型')
32
+ })
33
+
34
+ test('Symbol.asyncDispose 已定义', () => {
35
+ assert(typeof Symbol.asyncDispose === 'symbol', 'Symbol.asyncDispose 应是 symbol 类型')
36
+ })
37
+
38
+ test('using —— 块退出时自动调用 Symbol.dispose', () => {
39
+ if (!supported) { assert(true, '(跳过:环境不支持 using 语法)'); return }
40
+ const log = []
41
+ new Function('log', 'Symbol', `
42
+ {
43
+ using r = {
44
+ [Symbol.dispose]() { log.push('disposed') }
45
+ }
46
+ log.push('in-block')
47
+ }
48
+ log.push('after-block')
49
+ `)(log, Symbol)
50
+ assert(log[0] === 'in-block', '块内代码应正常执行')
51
+ assert(log[1] === 'disposed', '块退出时应自动调用 dispose')
52
+ assert(log[2] === 'after-block', 'dispose 之后块后代码才继续执行')
53
+ })
54
+
55
+ test('using —— 异常时也会调用 dispose(保证清理)', () => {
56
+ if (!supported) { assert(true, '(跳过)'); return }
57
+ const log = []
58
+ try {
59
+ new Function('log', 'Symbol', `
60
+ {
61
+ using r = { [Symbol.dispose]() { log.push('cleanup') } }
62
+ throw new Error('意外错误')
63
+ }
64
+ `)(log, Symbol)
65
+ } catch {}
66
+ assert(log[0] === 'cleanup', '即使抛出异常也应执行 dispose 清理')
67
+ })
68
+
69
+ test('多个 using 资源按 LIFO 顺序释放', () => {
70
+ if (!supported) { assert(true, '(跳过)'); return }
71
+ const order = []
72
+ new Function('order', 'Symbol', `
73
+ {
74
+ using a = { [Symbol.dispose]() { order.push('A') } }
75
+ using b = { [Symbol.dispose]() { order.push('B') } }
76
+ using c = { [Symbol.dispose]() { order.push('C') } }
77
+ }
78
+ `)(order, Symbol)
79
+ // 后声明的先释放(栈顺序)
80
+ assert(order.join(',') === 'C,B,A', '多资源应按 LIFO(后进先出)顺序释放')
81
+ })
82
+
83
+ test('await using —— 异步 dispose', async () => {
84
+ if (!supported) { assert(true, '(跳过)'); return }
85
+ const log = []
86
+ const fn = new Function('log', 'Symbol', `
87
+ return (async () => {
88
+ {
89
+ await using r = {
90
+ async [Symbol.asyncDispose]() {
91
+ await new Promise(res => setTimeout(res, 0))
92
+ log.push('async-disposed')
93
+ }
94
+ }
95
+ log.push('in-block')
96
+ }
97
+ log.push('after-block')
98
+ })()
99
+ `)
100
+ await fn(log, Symbol)
101
+ assert(log[0] === 'in-block', '异步块内应正常执行')
102
+ assert(log[1] === 'async-disposed', 'await using 应等待异步 dispose 完成')
103
+ assert(log[2] === 'after-block', 'dispose 之后才继续执行块后代码')
104
+ })
105
+
106
+ test('DisposableStack —— 手动管理资源栈', () => {
107
+ if (typeof DisposableStack === 'undefined') { assert(true, '(跳过:环境不支持 DisposableStack)'); return }
108
+ const log = []
109
+ const stack = new DisposableStack()
110
+ stack.defer(() => log.push('deferred-1'))
111
+ stack.defer(() => log.push('deferred-2'))
112
+ stack.dispose()
113
+ assert(log.join(',') === 'deferred-2,deferred-1', 'DisposableStack 应按 LIFO 执行')
114
+ })
115
+
116
+ return getResults()
117
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * ES2026 —— Atomics.pause()
3
+ *
4
+ * Atomics.pause(iterationNumber?) 向 CPU 发出"自旋等待"提示,
5
+ * 告知处理器当前处于忙等循环中,允许其优化资源分配(降低功耗、避免流水线冲刷)。
6
+ *
7
+ * 类似于 x86 的 PAUSE 指令 / ARM 的 YIELD 指令。
8
+ *
9
+ * 用途:SharedArrayBuffer + Atomics 实现的多线程自旋锁(spinlock)。
10
+ * 注意:纯 JS 主线程中调用无害但也无实际效果,仅在 Worker 忙等场景有优化意义。
11
+ */
12
+ import { createSuite } from '../../utils/runner.js'
13
+
14
+ export function testAtomicsPause() {
15
+ const { test, assert, getResults } = createSuite('Atomics.pause() (ES2026)')
16
+
17
+ const supported = typeof Atomics.pause === 'function'
18
+
19
+ test('Atomics.pause 函数存在', () => {
20
+ if (!supported) { assert(true, '(跳过:环境不支持 Atomics.pause)'); return }
21
+ assert(typeof Atomics.pause === 'function', 'Atomics.pause 应为函数')
22
+ })
23
+
24
+ test('无参数调用不抛出错误', () => {
25
+ if (!supported) { assert(true, '(跳过)'); return }
26
+ let threw = false
27
+ try { Atomics.pause() } catch { threw = true }
28
+ assert(!threw, 'Atomics.pause() 无参数调用不应抛出')
29
+ })
30
+
31
+ test('传入整数参数不抛出错误', () => {
32
+ if (!supported) { assert(true, '(跳过)'); return }
33
+ let threw = false
34
+ try {
35
+ Atomics.pause(0)
36
+ Atomics.pause(1)
37
+ Atomics.pause(100)
38
+ } catch { threw = true }
39
+ assert(!threw, 'Atomics.pause(N) 应接受非负整数参数')
40
+ })
41
+
42
+ test('返回值为 undefined', () => {
43
+ if (!supported) { assert(true, '(跳过)'); return }
44
+ assert(Atomics.pause() === undefined, 'Atomics.pause() 应返回 undefined')
45
+ })
46
+
47
+ test('自旋等待模式下连续调用不崩溃', () => {
48
+ if (!supported) { assert(true, '(跳过)'); return }
49
+ // 模拟自旋锁循环:每次迭代调用 pause 并递增计数
50
+ let i = 0
51
+ for (; i < 10; i++) {
52
+ Atomics.pause(i) // 传入迭代次数,允许实现做自适应暂停
53
+ }
54
+ assert(i === 10, '10 次自旋循环应正常完成')
55
+ })
56
+
57
+ test('与 SharedArrayBuffer + Atomics.load 配合(模拟场景)', () => {
58
+ if (!supported) { assert(true, '(跳过)'); return }
59
+ if (typeof SharedArrayBuffer === 'undefined') {
60
+ assert(true, '(跳过:环境不支持 SharedArrayBuffer)')
61
+ return
62
+ }
63
+ // 创建一个共享标志位,初始为 0
64
+ const buf = new SharedArrayBuffer(4)
65
+ const flag = new Int32Array(buf)
66
+ flag[0] = 1 // 模拟"锁已被占用"
67
+
68
+ let spinCount = 0
69
+ const MAX_SPIN = 5
70
+ // 模拟自旋等待:检查标志位,若被占用则 pause 后重试
71
+ while (Atomics.load(flag, 0) !== 0 && spinCount < MAX_SPIN) {
72
+ Atomics.pause(spinCount)
73
+ spinCount++
74
+ if (spinCount === 3) Atomics.store(flag, 0, 0) // 模拟锁被释放
75
+ }
76
+ assert(Atomics.load(flag, 0) === 0, '自旋等待后标志位应变为 0(锁释放)')
77
+ assert(spinCount === 3, '应在第 3 次迭代时检测到锁释放')
78
+ })
79
+
80
+ return getResults()
81
+ }