Skip to content
easy
闭包计数器

counter

请创建一个含有属性count的object。count 的初始值为0,每次取值的时候自增1。

js
const counter = createCounter()
counter.count // 0
counter.count // 1
counter.count // 2
counter.count = 100 // 无法更改
counter.count // 3

题目模版

js
export default function createCounter() {}

测试代码

js
// tests/createCounter.test.js
import { describe, expect, it } from 'vitest'
import createCounter from './createCounter.js'

describe('createCounter', () => {
  it('首次读取 count 返回 0,之后递增:0,1,2,3...', () => {
    const c = createCounter()
    expect(c.count).toBe(0)
    expect(c.count).toBe(1)
    expect(c.count).toBe(2)
    expect(c.count).toBe(3)
  })

  it('读取非 count 属性不应影响计数', () => {
    const c = createCounter()
    // 首次读取 count -> 0
    expect(c.count).toBe(0)

    // 读取其他不存在的属性应返回 undefined,不触发计数变化
    expect(c.foo).toBeUndefined()
    expect(c.bar).toBeUndefined()

    // 计数应继续从 1 开始
    expect(c.count).toBe(1)
  })

  it('设置非 count 属性应成功且不影响计数', () => {
    const c = createCounter()
    expect(c.count).toBe(0)

    c.name = 'alice'
    c.age = 20

    expect(c.name).toBe('alice')
    expect(c.age).toBe(20)

    // 计数继续递增
    expect(c.count).toBe(1)
    expect(c.count).toBe(2)
  })

  it('直接对 count 赋值应在严格模式抛出 TypeError,且不改变计数进度', () => {
    const c = createCounter()
    expect(c.count).toBe(0)
    expect(c.count).toBe(1)

    // 模块/测试文件默认是严格模式;handler.set 对 'count' 未返回 true,会抛 TypeError
    expect(() => {
      c.count = 100
    }).toThrow(TypeError)

    // 赋值失败不应改变计数轨迹
    expect(c.count).toBe(2)
    expect(c.count).toBe(3)
  })

  it('删除 count 后再次读取应重置为 0(因为 get 会重新初始化)', () => {
    const c = createCounter()
    expect(c.count).toBe(0)
    expect(c.count).toBe(1)

    // delete 走默认行为(未定义 deleteProperty trap),会真删除目标属性
    const deleted = delete c.count
    expect(deleted).toBe(true)

    // 删除后再次第一次读取,应重新初始化为 0
    expect(c.count).toBe(0)
    expect(c.count).toBe(1)
  })

  it('首次读取后,count 应成为目标对象自有属性(可检查 descriptor)', () => {
    const c = createCounter()
    // 触发创建并返回 0
    expect(c.count).toBe(0)

    // 现在目标对象应具备自有属性 'count'
    // 由于 c 是 Proxy,直接从代理上拿 descriptor 不一定稳定;
    // 但这里 Reflect.getOwnPropertyDescriptor 应该透传到 target。
    const desc = Reflect.getOwnPropertyDescriptor(c, 'count')
    expect(desc).toBeDefined()
    expect(desc?.writable).toBe(true)
    expect(desc?.enumerable).toBe(true)
    expect(desc?.configurable).toBe(true)
  })

  it('多次交错访问与设置其它键,计数行为仍应稳定', () => {
    const c = createCounter()
    expect(c.count).toBe(0) // 0
    c.x = 1
    expect(c.y).toBeUndefined()
    expect(c.count).toBe(1) // 1
    c.z = { k: 9 }
    expect(c.z.k).toBe(9)
    expect(c.count).toBe(2) // 2
    expect(c.count).toBe(3) // 3
  })
})

答案

类型路径
JS 版本problems/Day 23/answer.js
TS 版本待补充
Review待补充

内容基于 MIT 许可 | 保持节奏 · 持续积累