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,54 @@
1
+ import { createSuite } from '../../utils/runner.js'
2
+
3
+ export function testLetConst() {
4
+ const { test, assert, getResults } = createSuite('let & const')
5
+
6
+ test('let 块级作用域', () => {
7
+ let results = []
8
+ for (let i = 0; i < 3; i++) {
9
+ results.push(i)
10
+ }
11
+ assert(results[0] === 0 && results[2] === 2, 'let 循环变量应为独立值')
12
+ })
13
+
14
+ test('let 不可重复声明', () => {
15
+ try {
16
+ // 用 Function 构造器在独立作用域测试
17
+ new Function('"use strict"; let x = 1; let x = 2;')()
18
+ assert(false, '应抛出 SyntaxError')
19
+ } catch (e) {
20
+ assert(e instanceof SyntaxError || e instanceof TypeError, '应为语法错误')
21
+ }
22
+ })
23
+
24
+ test('let 暂时性死区(TDZ)', () => {
25
+ try {
26
+ new Function('"use strict"; console.log(y); let y = 1;')()
27
+ assert(false, '应抛出 ReferenceError')
28
+ } catch (e) {
29
+ assert(true)
30
+ }
31
+ })
32
+
33
+ test('const 声明常量', () => {
34
+ const PI = 3.14159
35
+ assert(PI === 3.14159, 'const 值应保持不变')
36
+ })
37
+
38
+ test('const 不可重新赋值', () => {
39
+ try {
40
+ new Function('"use strict"; const x = 1; x = 2;')()
41
+ assert(false, '应抛出 TypeError')
42
+ } catch (e) {
43
+ assert(e instanceof TypeError, '应为 TypeError')
44
+ }
45
+ })
46
+
47
+ test('const 对象属性可修改', () => {
48
+ const obj = { a: 1 }
49
+ obj.a = 2
50
+ assert(obj.a === 2, 'const 对象的属性可以修改')
51
+ })
52
+
53
+ return getResults()
54
+ }
@@ -0,0 +1,68 @@
1
+ import { createSuite } from '../../utils/runner.js'
2
+
3
+ export function testArrowFunctions() {
4
+ const { test, assert, getResults } = createSuite('箭头函数')
5
+
6
+ test('基本语法', () => {
7
+ const add = (a, b) => a + b
8
+ assert(add(2, 3) === 5, '箭头函数返回值应为 5')
9
+ })
10
+
11
+ test('单参数省略括号', () => {
12
+ const double = n => n * 2
13
+ assert(double(4) === 8, '单参数箭头函数应正常工作')
14
+ })
15
+
16
+ test('无参数需要括号', () => {
17
+ const greet = () => 'hello'
18
+ assert(greet() === 'hello', '无参数箭头函数应正常工作')
19
+ })
20
+
21
+ test('多行函数体需要花括号和 return', () => {
22
+ const calc = (a, b) => {
23
+ const sum = a + b
24
+ return sum * 2
25
+ }
26
+ assert(calc(2, 3) === 10, '多行箭头函数应正确计算')
27
+ })
28
+
29
+ test('返回对象字面量需要括号', () => {
30
+ const makeObj = (x) => ({ value: x })
31
+ const obj = makeObj(42)
32
+ assert(obj.value === 42, '箭头函数返回对象应正确包裹')
33
+ })
34
+
35
+ test('this 词法绑定', () => {
36
+ function Timer() {
37
+ this.count = 0
38
+ const tick = () => {
39
+ this.count++
40
+ }
41
+ tick()
42
+ tick()
43
+ }
44
+ const t = new Timer()
45
+ assert(t.count === 2, '箭头函数 this 应绑定外层作用域')
46
+ })
47
+
48
+ test('没有 arguments 对象', () => {
49
+ const fn = () => typeof arguments === 'undefined' || true
50
+ // 在模块顶层 arguments 未定义,所以箭头函数内也未定义
51
+ assert(true, '箭头函数无独立 arguments')
52
+ })
53
+
54
+ test('不能用作构造函数(原生环境)', () => {
55
+ const Fn = () => {}
56
+ // Babel 将箭头函数编译为普通函数时,此限制在运行时不可复现,故跳过检测
57
+ const isNativeArrow = Fn.prototype === undefined
58
+ if (!isNativeArrow) return // 编译降级环境跳过
59
+ try {
60
+ new Fn()
61
+ assert(false, '应抛出 TypeError')
62
+ } catch (e) {
63
+ assert(e instanceof TypeError, '箭头函数不能 new')
64
+ }
65
+ })
66
+
67
+ return getResults()
68
+ }
@@ -0,0 +1,51 @@
1
+ import { createSuite } from '../../utils/runner.js'
2
+
3
+ export function testTemplateLiterals() {
4
+ const { test, assert, getResults } = createSuite('模板字符串')
5
+
6
+ test('基本插值', () => {
7
+ const name = 'ES2015'
8
+ const str = `Hello, ${name}!`
9
+ assert(str === 'Hello, ES2015!', '模板字符串插值应正确')
10
+ })
11
+
12
+ test('表达式插值', () => {
13
+ const a = 3, b = 4
14
+ const str = `${a} + ${b} = ${a + b}`
15
+ assert(str === '3 + 4 = 7', '模板字符串支持表达式')
16
+ })
17
+
18
+ test('多行字符串', () => {
19
+ const multi = `第一行
20
+ 第二行`
21
+ assert(multi.includes('\n'), '模板字符串支持多行')
22
+ })
23
+
24
+ test('嵌套模板', () => {
25
+ const items = ['a', 'b', 'c']
26
+ const list = `items: ${items.map(i => `[${i}]`).join(', ')}`
27
+ assert(list === 'items: [a], [b], [c]', '模板字符串可以嵌套')
28
+ })
29
+
30
+ test('标签模板 - 基本用法', () => {
31
+ function tag(strings, ...values) {
32
+ return strings.raw[0] + values[0].toUpperCase()
33
+ }
34
+ const name = 'world'
35
+ const result = tag`hello ${name}`
36
+ assert(result === 'hello WORLD', '标签模板应正确处理')
37
+ })
38
+
39
+ test('标签模板 - String.raw', () => {
40
+ const path = String.raw`C:\Users\test\n`
41
+ assert(path.includes('\\n'), 'String.raw 应保留原始反斜杠')
42
+ })
43
+
44
+ test('模板字符串中调用函数', () => {
45
+ const upper = (s) => s.toUpperCase()
46
+ const str = `result: ${upper('hello')}`
47
+ assert(str === 'result: HELLO', '模板字符串中可调用函数')
48
+ })
49
+
50
+ return getResults()
51
+ }
@@ -0,0 +1,68 @@
1
+ import { createSuite } from '../../utils/runner.js'
2
+
3
+ export function testDestructuring() {
4
+ const { test, assert, getResults } = createSuite('解构赋值')
5
+
6
+ test('数组解构 - 基本', () => {
7
+ const [a, b, c] = [1, 2, 3]
8
+ assert(a === 1 && b === 2 && c === 3, '数组解构基本用法')
9
+ })
10
+
11
+ test('数组解构 - 跳过元素', () => {
12
+ const [, second, , fourth] = [1, 2, 3, 4]
13
+ assert(second === 2 && fourth === 4, '数组解构可跳过元素')
14
+ })
15
+
16
+ test('数组解构 - 默认值', () => {
17
+ const [x = 10, y = 20] = [1]
18
+ assert(x === 1 && y === 20, '数组解构默认值')
19
+ })
20
+
21
+ test('数组解构 - rest 元素', () => {
22
+ const [first, ...rest] = [1, 2, 3, 4]
23
+ assert(first === 1 && rest.length === 3, '数组解构 rest 元素')
24
+ })
25
+
26
+ test('数组解构 - 交换变量', () => {
27
+ let p = 1, q = 2;
28
+ [p, q] = [q, p]
29
+ assert(p === 2 && q === 1, '解构交换变量')
30
+ })
31
+
32
+ test('对象解构 - 基本', () => {
33
+ const { name, age } = { name: 'Alice', age: 25 }
34
+ assert(name === 'Alice' && age === 25, '对象解构基本用法')
35
+ })
36
+
37
+ test('对象解构 - 重命名', () => {
38
+ const { name: userName, age: userAge } = { name: 'Bob', age: 30 }
39
+ assert(userName === 'Bob' && userAge === 30, '对象解构重命名')
40
+ })
41
+
42
+ test('对象解构 - 默认值', () => {
43
+ const { a = 1, b = 2 } = { a: 10 }
44
+ assert(a === 10 && b === 2, '对象解构默认值')
45
+ })
46
+
47
+ test('对象解构 - rest 属性', () => {
48
+ const { x, ...others } = { x: 1, y: 2, z: 3 }
49
+ assert(x === 1 && others.y === 2 && others.z === 3, '对象解构 rest 属性')
50
+ })
51
+
52
+ test('嵌套解构', () => {
53
+ const { a: { b: { c } } } = { a: { b: { c: 42 } } }
54
+ assert(c === 42, '嵌套解构')
55
+ })
56
+
57
+ test('函数参数解构', () => {
58
+ const fn = ({ name, age = 18 }) => `${name}:${age}`
59
+ assert(fn({ name: 'Alice' }) === 'Alice:18', '函数参数解构')
60
+ })
61
+
62
+ test('字符串解构', () => {
63
+ const [a, b, c] = 'abc'
64
+ assert(a === 'a' && b === 'b' && c === 'c', '字符串解构')
65
+ })
66
+
67
+ return getResults()
68
+ }
@@ -0,0 +1,87 @@
1
+ import { createSuite } from '../../utils/runner.js'
2
+
3
+ export function testDefaultRestSpread() {
4
+ const { test, assert, getResults } = createSuite('默认参数/Rest/Spread')
5
+
6
+ // 默认参数
7
+ test('默认参数 - 基本', () => {
8
+ const greet = (name = 'World') => `Hello, ${name}!`
9
+ assert(greet() === 'Hello, World!', '默认参数应生效')
10
+ assert(greet('Alice') === 'Hello, Alice!', '传参时默认参数应被覆盖')
11
+ })
12
+
13
+ test('默认参数 - undefined 触发默认值', () => {
14
+ const fn = (a = 1) => a
15
+ assert(fn(undefined) === 1, 'undefined 触发默认参数')
16
+ assert(fn(null) === null, 'null 不触发默认参数')
17
+ })
18
+
19
+ test('默认参数 - 使用前面的参数', () => {
20
+ const fn = (a, b = a * 2) => b
21
+ assert(fn(5) === 10, '默认参数可引用前面的参数')
22
+ })
23
+
24
+ test('默认参数 - 表达式', () => {
25
+ const fn = (x = Math.random()) => x
26
+ const result = fn()
27
+ assert(typeof result === 'number', '默认参数可以是表达式')
28
+ })
29
+
30
+ // Rest 参数
31
+ test('rest 参数 - 收集剩余参数', () => {
32
+ const sum = (first, ...rest) => rest.reduce((acc, n) => acc + n, first)
33
+ assert(sum(1, 2, 3, 4) === 10, 'rest 参数应收集剩余参数')
34
+ })
35
+
36
+ test('rest 参数 - 是真正的数组', () => {
37
+ const fn = (...args) => Array.isArray(args)
38
+ assert(fn(1, 2, 3) === true, 'rest 参数是真正的数组')
39
+ })
40
+
41
+ test('rest 参数只能在最后', () => {
42
+ try {
43
+ new Function('function f(...a, b){}')()
44
+ assert(false, '应抛出 SyntaxError')
45
+ } catch (e) {
46
+ assert(true, 'rest 参数只能在最后')
47
+ }
48
+ })
49
+
50
+ // Spread 操作符
51
+ test('spread 展开数组', () => {
52
+ const a = [1, 2, 3]
53
+ const b = [0, ...a, 4]
54
+ assert(b.join(',') === '0,1,2,3,4', 'spread 展开数组')
55
+ })
56
+
57
+ test('spread 复制数组', () => {
58
+ const original = [1, 2, 3]
59
+ const copy = [...original]
60
+ copy.push(4)
61
+ assert(original.length === 3 && copy.length === 4, 'spread 复制数组为浅拷贝')
62
+ })
63
+
64
+ test('spread 合并数组', () => {
65
+ const merged = [...[1, 2], ...[3, 4], ...[5]]
66
+ assert(merged.join(',') === '1,2,3,4,5', 'spread 合并数组')
67
+ })
68
+
69
+ test('spread 传入函数参数', () => {
70
+ const nums = [1, 2, 3]
71
+ assert(Math.max(...nums) === 3, 'spread 传入函数参数')
72
+ })
73
+
74
+ test('spread 展开对象', () => {
75
+ const obj1 = { a: 1, b: 2 }
76
+ const obj2 = { ...obj1, c: 3 }
77
+ assert(obj2.a === 1 && obj2.b === 2 && obj2.c === 3, 'spread 展开对象')
78
+ })
79
+
80
+ test('spread 对象覆盖属性', () => {
81
+ const base = { a: 1, b: 2 }
82
+ const extended = { ...base, b: 99 }
83
+ assert(extended.b === 99, 'spread 后面属性覆盖前面')
84
+ })
85
+
86
+ return getResults()
87
+ }
@@ -0,0 +1,100 @@
1
+ import { createSuite } from '../../utils/runner.js'
2
+
3
+ export function testClasses() {
4
+ const { test, assert, getResults } = createSuite('类(Class)')
5
+
6
+ test('基本类定义', () => {
7
+ class Animal {
8
+ constructor(name) {
9
+ this.name = name
10
+ }
11
+ speak() {
12
+ return `${this.name} makes a sound.`
13
+ }
14
+ }
15
+ const a = new Animal('Cat')
16
+ assert(a.name === 'Cat', '构造函数应正确赋值')
17
+ assert(a.speak() === 'Cat makes a sound.', '实例方法应正常工作')
18
+ })
19
+
20
+ test('类继承', () => {
21
+ class Animal {
22
+ constructor(name) { this.name = name }
23
+ speak() { return `${this.name}: ...` }
24
+ }
25
+ class Dog extends Animal {
26
+ speak() { return `${this.name}: Woof!` }
27
+ }
28
+ const d = new Dog('Rex')
29
+ assert(d.speak() === 'Rex: Woof!', '子类应覆盖父类方法')
30
+ assert(d instanceof Dog && d instanceof Animal, 'instanceof 检查应通过')
31
+ })
32
+
33
+ test('super 调用父类方法', () => {
34
+ class Shape {
35
+ area() { return 0 }
36
+ describe() { return `面积: ${this.area()}` }
37
+ }
38
+ class Circle extends Shape {
39
+ constructor(r) { super(); this.r = r }
40
+ area() { return Math.PI * this.r * this.r }
41
+ describe() { return super.describe() + ` (圆形)` }
42
+ }
43
+ const c = new Circle(1)
44
+ assert(c.describe().includes('面积:'), 'super 方法调用')
45
+ })
46
+
47
+ test('静态方法', () => {
48
+ class MathUtil {
49
+ static add(a, b) { return a + b }
50
+ static PI = 3.14159
51
+ }
52
+ assert(MathUtil.add(2, 3) === 5, '静态方法应直接通过类调用')
53
+ assert(MathUtil.PI === 3.14159, '静态属性应正确设置')
54
+ })
55
+
56
+ test('getter 和 setter', () => {
57
+ class Temperature {
58
+ constructor(celsius) { this._celsius = celsius }
59
+ get fahrenheit() { return this._celsius * 9 / 5 + 32 }
60
+ set fahrenheit(f) { this._celsius = (f - 32) * 5 / 9 }
61
+ }
62
+ const t = new Temperature(0)
63
+ assert(t.fahrenheit === 32, 'getter 应正确计算')
64
+ t.fahrenheit = 212
65
+ assert(Math.round(t._celsius) === 100, 'setter 应正确转换')
66
+ })
67
+
68
+ test('私有字段 (#)', () => {
69
+ class Counter {
70
+ #count = 0
71
+ increment() { this.#count++ }
72
+ get value() { return this.#count }
73
+ }
74
+ const c = new Counter()
75
+ c.increment()
76
+ c.increment()
77
+ assert(c.value === 2, '私有字段应正确工作')
78
+ assert(!('count' in c), '私有字段不可从外部访问')
79
+ })
80
+
81
+ test('类表达式', () => {
82
+ const Rect = class Rectangle {
83
+ constructor(w, h) { this.w = w; this.h = h }
84
+ area() { return this.w * this.h }
85
+ }
86
+ const r = new Rect(3, 4)
87
+ assert(r.area() === 12, '类表达式应正常工作')
88
+ })
89
+
90
+ test('类不存在变量提升', () => {
91
+ try {
92
+ new Function('"use strict"; new Foo(); class Foo {}')()
93
+ assert(false, '应抛出 ReferenceError')
94
+ } catch (e) {
95
+ assert(true, '类不提升,使用前必须先声明')
96
+ }
97
+ })
98
+
99
+ return getResults()
100
+ }
@@ -0,0 +1,79 @@
1
+ import { createSuite } from '../../utils/runner.js'
2
+
3
+ export async function testPromises() {
4
+ const { test, assert, getResults } = createSuite('Promise')
5
+
6
+ const tests = []
7
+
8
+ tests.push(test('基本 Promise resolve', () =>
9
+ new Promise((resolve) => resolve(42)).then((v) => assert(v === 42, 'resolve 值应为 42'))
10
+ ))
11
+
12
+ tests.push(test('基本 Promise reject', () =>
13
+ new Promise((_, reject) => reject(new Error('失败'))).catch((e) =>
14
+ assert(e.message === '失败', 'reject 应携带错误信息')
15
+ )
16
+ ))
17
+
18
+ tests.push(test('Promise 链式调用', () =>
19
+ Promise.resolve(1)
20
+ .then((v) => v + 1)
21
+ .then((v) => v * 3)
22
+ .then((v) => assert(v === 6, '链式调用结果应为 6'))
23
+ ))
24
+
25
+ tests.push(test('Promise.all - 全部成功', () =>
26
+ Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]).then((values) =>
27
+ assert(values.join(',') === '1,2,3', 'Promise.all 应返回所有结果')
28
+ )
29
+ ))
30
+
31
+ tests.push(test('Promise.all - 一个失败则全部失败', () =>
32
+ Promise.all([Promise.resolve(1), Promise.reject(new Error('error')), Promise.resolve(3)])
33
+ .catch((e) => assert(e.message === 'error', 'Promise.all 中有失败应 reject'))
34
+ ))
35
+
36
+ tests.push(test('Promise.race - 返回最快的结果', () => {
37
+ const p1 = new Promise((r) => setTimeout(() => r('slow'), 100))
38
+ const p2 = Promise.resolve('fast')
39
+ return Promise.race([p1, p2]).then((v) => assert(v === 'fast', 'Promise.race 应返回最快完成的'))
40
+ }))
41
+
42
+ tests.push(test('Promise.allSettled - 全部结果', () =>
43
+ Promise.allSettled([Promise.resolve(1), Promise.reject('err')]).then((results) => {
44
+ assert(results[0].status === 'fulfilled', 'allSettled fulfilled')
45
+ assert(results[1].status === 'rejected', 'allSettled rejected')
46
+ })
47
+ ))
48
+
49
+ tests.push(test('Promise.any - 返回第一个成功', () =>
50
+ Promise.any([Promise.reject('e1'), Promise.resolve('ok'), Promise.resolve('ok2')]).then((v) =>
51
+ assert(v === 'ok', 'Promise.any 应返回第一个成功的值')
52
+ )
53
+ ))
54
+
55
+ tests.push(test('async/await 基本用法', async () => {
56
+ async function fetchData() {
57
+ const result = await Promise.resolve('data')
58
+ return result
59
+ }
60
+ const data = await fetchData()
61
+ assert(data === 'data', 'async/await 应正确获取异步结果')
62
+ }))
63
+
64
+ tests.push(test('async/await 错误处理', async () => {
65
+ async function failFn() {
66
+ throw new Error('async error')
67
+ }
68
+ try {
69
+ await failFn()
70
+ assert(false, '应抛出错误')
71
+ } catch (e) {
72
+ assert(e.message === 'async error', 'async/await 错误处理')
73
+ }
74
+ }))
75
+
76
+ await Promise.all(tests.filter(Boolean))
77
+
78
+ return getResults()
79
+ }
@@ -0,0 +1,85 @@
1
+ import { createSuite } from '../../utils/runner.js'
2
+
3
+ export function testSymbols() {
4
+ const { test, assert, getResults } = createSuite('Symbol')
5
+
6
+ test('Symbol 唯一性', () => {
7
+ const s1 = Symbol('desc')
8
+ const s2 = Symbol('desc')
9
+ assert(s1 !== s2, '相同描述的 Symbol 不相等')
10
+ })
11
+
12
+ test('Symbol 描述属性', () => {
13
+ const s = Symbol('my-symbol')
14
+ assert(s.description === 'my-symbol', 'Symbol.description 应返回描述字符串')
15
+ })
16
+
17
+ test('Symbol 作为对象属性键', () => {
18
+ const key = Symbol('key')
19
+ const obj = { [key]: 'value' }
20
+ assert(obj[key] === 'value', 'Symbol 可作为对象属性键')
21
+ })
22
+
23
+ test('Symbol 属性不被普通枚举', () => {
24
+ const sym = Symbol('hidden')
25
+ const obj = { [sym]: 1, visible: 2 }
26
+ assert(!Object.keys(obj).includes(sym.toString()), 'Symbol 属性不出现在 Object.keys 中')
27
+ assert(Object.getOwnPropertySymbols(obj).length === 1, 'getOwnPropertySymbols 可获取 Symbol 属性')
28
+ })
29
+
30
+ test('Symbol.for 全局注册', () => {
31
+ const s1 = Symbol.for('shared')
32
+ const s2 = Symbol.for('shared')
33
+ assert(s1 === s2, 'Symbol.for 应返回同一个 Symbol')
34
+ })
35
+
36
+ test('Symbol.keyFor 获取注册键', () => {
37
+ const s = Symbol.for('test-key')
38
+ assert(Symbol.keyFor(s) === 'test-key', 'Symbol.keyFor 应返回注册键')
39
+ const local = Symbol('local')
40
+ assert(Symbol.keyFor(local) === undefined, '未注册的 Symbol 返回 undefined')
41
+ })
42
+
43
+ test('内置 Symbol - Symbol.iterator', () => {
44
+ class Range {
45
+ constructor(start, end) { this.start = start; this.end = end }
46
+ [Symbol.iterator]() {
47
+ let current = this.start
48
+ const end = this.end
49
+ return {
50
+ next() {
51
+ return current <= end
52
+ ? { value: current++, done: false }
53
+ : { done: true }
54
+ }
55
+ }
56
+ }
57
+ }
58
+ const result = [...new Range(1, 3)]
59
+ assert(result.join(',') === '1,2,3', 'Symbol.iterator 自定义迭代器')
60
+ })
61
+
62
+ test('内置 Symbol - Symbol.toPrimitive', () => {
63
+ const obj = {
64
+ [Symbol.toPrimitive](hint) {
65
+ if (hint === 'number') return 42
66
+ if (hint === 'string') return 'forty-two'
67
+ return true
68
+ }
69
+ }
70
+ assert(+obj === 42, 'Symbol.toPrimitive number')
71
+ assert(`${obj}` === 'forty-two', 'Symbol.toPrimitive string')
72
+ })
73
+
74
+ test('Symbol.hasInstance', () => {
75
+ class EvenNumber {
76
+ static [Symbol.hasInstance](num) {
77
+ return Number.isInteger(num) && num % 2 === 0
78
+ }
79
+ }
80
+ assert(2 instanceof EvenNumber, '2 应为 EvenNumber 实例')
81
+ assert(!(3 instanceof EvenNumber), '3 不应为 EvenNumber 实例')
82
+ })
83
+
84
+ return getResults()
85
+ }