npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

object-hook

v0.1.0

Published

Path-based hooks for object methods in JavaScript and TypeScript.

Readme

object-hook

object-hook 是一个面向 JavaScript / TypeScript 的对象方法 Hook 工具库。

和常见的 hook / AOP 库相比,它最大的差异是:

  • 它的核心能力是直接按对象路径 Hook 现有方法,并且可以批量处理多个方法

如果你的目标是增强一个已经存在的对象结构,比如 actions.savelifecycles.readyservice.fetchUser 这类方法,object-hook 会比通用事件系统或重型 AOP 框架更直接。

它适合在不改业务调用方的前提下,为已有对象方法附加横切逻辑,比如:

  • 日志和埋点
  • 权限和前置校验
  • 参数预处理
  • 返回值转换
  • 错误拦截和收尾逻辑

安装

npm install object-hook

导入方式

推荐:

  • ESM / TypeScript: import {hook, createHook} from 'object-hook'
  • CommonJS: const {hook, createHook} = require('object-hook')

推荐优先使用命名导出,这样在 ESM / TypeScript / CommonJS 场景下都更一致。

默认导出 hook 仍然可用:

import hook from 'object-hook';

但作为公共文档和跨模块系统示例,优先使用命名导出更稳妥。

快速开始

import {hook} from 'object-hook';

const pageOptions = {
    data: {
        loaded: false
    },
    onLoad(query: Record<string, string>) {
        console.log('page onLoad', query);
        return query;
    }
};

hook(pageOptions, 'onLoad', {
    before(query) {
        console.log('before onLoad', query);
    },
    afterReturn(result) {
        return {
            ...result,
            hooked: true
        };
    }
});

pageOptions.onLoad({from: 'share'});

hook 做什么

hook() 是主入口。它会原地修改目标对象上的方法,并返回同一个对象。

你可以用它:

  • Hook 单个方法
  • Hook 多个路径,共用同一套 Hook 配置
  • Hook 多个路径,并为每个路径指定不同配置
  • 根据宿主对象动态生成 Hook 配置

支持的调用形式

Hook 单个方法

hook(service, 'fetchUser', {
    before(id) {
        console.log('before fetch', id);
    }
});

Hook 多个路径,共用同一套配置

hook(app, ['actions.save', 'actions.publish'], {
    before() {
        console.log('start action');
    }
});

Hook 多个路径,分别配置

hook(app, {
    'actions.save': {
        before() {
            console.log('saving');
        }
    },
    'lifecycles.ready': {
        afterReturn(result) {
            return result?.toUpperCase?.() ?? result;
        }
    }
});

Hook 生命周期

单个方法的 Hook 配置支持四个回调:

  • before(...args)
  • afterReturn(result, ...args)
  • afterThrow(error, ...args)
  • after(result, error, ...args)

调用时机

before

在原方法执行之前调用。

适合做:

  • 参数检查
  • 日志
  • 计时开始
  • 前置副作用

afterReturn

在原方法成功返回后调用。

适合做:

  • 转换返回值
  • 补充字段
  • 记录成功日志

它的返回值会成为最终返回值。

afterThrow

在原方法抛错后调用。

适合做:

  • 错误日志
  • 监控上报
  • 错误转换

如果 afterThrow 不重新抛出错误,则错误视为已被拦截,后续不会再抛出原错误。

after

在流程结束时调用。

适合做:

  • 关闭计时
  • 清理上下文
  • 统一收尾

它不改变最终返回值。

调用顺序

成功路径:

  1. before
  2. 原方法
  3. afterReturn
  4. after
  5. 返回最终结果

异常路径:

  1. before
  2. 原方法抛错
  3. afterThrow
  4. after
  5. 如果 afterThrow 没有重新抛错,则返回当前结果;否则继续抛错

顺序示例

hook(service, 'fetchUser', {
    before(id) {
        console.log('1.before', id);
    },
    afterReturn(result) {
        console.log('3.afterReturn', result);
        return {
            ...result,
            loadedAt: Date.now()
        };
    },
    afterThrow(error) {
        console.log('3.afterThrow', error);
        throw error;
    },
    after(result, error) {
        console.log('4.after', {result, error});
    }
});

thisthisArg

默认情况下,原方法和 Hook 回调共享同一个 this

const service = {
    name: 'user-service',
    fetchUser(id: string) {
        return `${this.name}:${id}`;
    }
};

hook(service, 'fetchUser', {
    before(this: any, id) {
        console.log(this.name, id); // user-service
    }
});

如果传入 thisArg,原方法和 Hook 回调都会在这个 thisArg 上执行。

const customThis = {name: 'custom-service'};

hook(service, 'fetchUser', {
    before(this: any, id) {
        console.log(this.name, id); // custom-service
    }
}, customThis);

严格模式

默认情况下,hook() 使用严格模式:

  • 路径不存在会抛错
  • 目标存在但不是函数会抛错
  • 不会静默创建对象或空方法
hook(app, 'actions.missing', {
    before() {}
});
// Error: Cannot hook "actions.missing": path does not exist
hook(app, 'config.data', {
    before() {}
});
// Error: Cannot hook "config.data": target is not a function

这种默认行为更适合业务代码,因为它能尽早暴露路径拼写错误和对象结构变更。

createHook(options)

createHook() 用来创建自定义 Hook 函数。

import {createHook} from 'object-hook';

const compatHook = createHook({
    separator: '#',
    allowMissing: true,
    allowCreate: false
});

支持这些选项:

  • separator: 路径分隔符,默认 '.'
  • allowMissing: 路径不存在时跳过,默认 false
  • allowCreate: 路径不存在时创建缺失节点和目标方法,默认 false

兼容模式

如果你需要兼容宽松行为,可以显式创建一个非严格版本:

const compatHook = createHook({
    allowMissing: true,
    allowCreate: true
});

推荐做法:

  • 新代码保持默认严格模式
  • 只有在迁移旧代码或兼容动态对象结构时才开启 allowMissing / allowCreate

Builder 用法

hook() 的 decoration 是函数时,它收到的参数是宿主对象,不是被 Hook 的原方法。

这个函数不是 beforeafterReturn 回调,而是一个 builder,用来生成真正的 Hook 配置。

它的返回值含义是:

  • 返回 false:跳过当前路径,不做 Hook
  • 返回对象:这个对象会被当成真正的 Hook 配置
  • 返回 undefinednull:等价于跳过当前路径,不做 Hook

这个 builder 只会在建立 Hook 时执行一次,不会在每次目标方法调用时重复执行。

hook(app, {
    'actions.save'(host) {
        return {
            before() {
                console.log(host);
            }
        };
    }
});

这适合按宿主对象状态动态决定是否启用 Hook,或者动态生成 Hook 配置。

hook(app, {
    'actions.save'(host) {
        return host.readonly
            ? false
            : {
                before() {
                    console.log('save allowed');
                }
            };
    }
});

如果 builder 返回 falseundefinednull,当前路径不会被 Hook。

其他 API

around(srcFunc, decoration, thisArg?)

为单个函数添加 before / afterReturn / afterThrow / after 生命周期 Hook。

import {around} from 'object-hook';

const wrapped = around(
    () => {
        throw new Error('Oops');
    },
    {
        before: () => console.log('before'),
        afterThrow: (error) => console.log('caught', error),
        after: () => console.log('finally')
    }
);

decorate(srcFunc, decoration, thisArg?)

底层函数装饰器。你可以完全接管原函数调用过程。

wrap(srcFunc, decoration, thisArg?)

低级 API。根据 decoration 形态自动调用 decoratearound

包消费说明

  • ESM 环境优先走 exports.import,入口是 dist/index.modern.mjs
  • CommonJS 环境走 exports.require,可直接通过命名导出拿到 hook
  • 类型声明统一来自 dist/index.d.ts

推荐把命名导出当成跨模块系统最稳定的写法;默认导出仅作为兼容入口保留。

测试

npm test

许可

MIT