diff --git a/src/createStore.ts b/src/createStore.ts index 31c9d20..4bf428a 100644 --- a/src/createStore.ts +++ b/src/createStore.ts @@ -1,42 +1,52 @@ +import { clone } from './clone'; import { createEmitter } from './createEmitter'; import { isFunction } from './isFunction'; import { isObject } from './isObject'; import { omit } from './omit'; import { Obj } from './types'; +type Store = T & { subscribe(fn: (s: T) => void): () => never }; + const { emit, on } = createEmitter(); +const { defineProperty, getOwnPropertyDescriptor } = Object; -const handler = () => ({ - get(obj: Obj, key: string) { - if (key === '_isProxy') return true; - const d = Object.getOwnPropertyDescriptor(obj, key); - const nok = isObject(obj[key]) && !obj[key]._isProxy && d?.writable; - nok && (obj[key] = new Proxy(obj[key], handler())); - return obj[key]; - }, - set(obj: Obj, key: string, val: unknown) { - if (obj[key] !== val) (obj[key] = val), emit('update'); - return true; - }, - deleteProperty(obj: Obj, key: string) { - delete obj[key]; - emit('update'); - return true; - }, -}); - -export function createStore(init: T): T & { subscribe(fn: (s: T) => void): () => never } { - const fns = []; - const subscribe = (fn: Function) => { - if (!fns.includes(fn)) fns.push(fn); - return () => { - const idx = fns.findIndex((l) => l === fn); - idx !== -1 && fns.splice(idx, 1); - }; +function handler() { + return { + get(obj: Obj, key: string) { + if (key === '_isProxy') return true; + const d = getOwnPropertyDescriptor(obj, key); + const nok = isObject(obj[key]) && !obj[key]._isProxy && d?.writable; + nok && (obj[key] = new Proxy(obj[key], handler())); + return obj[key]; + }, + set(obj: Obj, key: string, val: unknown) { + if (obj[key] !== val) (obj[key] = val), emit('update'); + return true; + }, + deleteProperty(obj: Obj, key: string) { + delete obj[key]; + emit('update'); + return true; + }, }; +} + +export function createStore(data: T): Store { + const fns = []; + const init = clone(data); + + defineProperty(init, 'subscribe', { + value(fn: Function) { + if (!fns.includes(fn)) fns.push(fn); + return () => { + const idx = fns.findIndex((l) => l === fn); + idx !== -1 && fns.splice(idx, 1); + }; + }, + }); let nextTickId = 0; - const store = new Proxy(Object.assign(init as never, { subscribe }), handler()); + const store = new Proxy(init, handler()); on('update', () => { const data = omit('subscribe', store); if (nextTickId) cancelAnimationFrame(nextTickId); @@ -44,20 +54,17 @@ export function createStore(init: T): T & { subscribe(fn: (s: T) => voi }); for (const prop in init) { - const d = Object.getOwnPropertyDescriptor(init, prop); + const d = getOwnPropertyDescriptor(init, prop); if (d?.get && d?.configurable) { - Object.defineProperty(init, prop, { + defineProperty(init, prop, { get: d.get.bind(store), - configurable: false, }); } else if (isFunction(init[prop])) { - Object.defineProperty(init, prop, { + defineProperty(init, prop, { value: (init[prop] as any).bind(store), - configurable: false, - writable: false, }); } } - return store as never; + return store as Store; }