我使用Node.js创建了一个API,我不想更改API,也不想在函数中添加额外的参数。但是,库中的内部代码现在需要在内部API方法和外部方法之间发送一些元数据。
有没有办法在JS中以某种方式在函数之间传递(元)数据而不涉及参数/参数?
TL; DR,为了JS API的目的,在函数之间传递元数据非常有用,它不应该改变签名。
(一个技巧是如果每次调用函数时都会创建它,你可以将数据分配给函数对象本身,但在这种情况下不是这样(每次调用时都不会创建函数)。)
我目前正在使用的技巧 - 而不是一个好的 - 在API中使用了一个options {}对象。我在该对象对象“__preParsed”中传递一个隐藏属性。用户将像往常一样使用该对象对象,在幕后我将它用于一些他们不需要了解的簿记内容。
好的,这里是代码:
// public API
beforeEach.cb = function (desc, opts, fn) {
const _args = pragmatik.parse(arguments, rules.hookSignature);
_args[ 1 ].cb = true;
return beforeEach.apply(ctx, _args);
};
beforeEach = function (desc, opts, aBeforeEach) {
handleSetupComplete(zuite);
const _args = pragmatik.parse(arguments, rules.hookSignature);
const obj = { //have to do this until destructuring works
desc: _args[ 0 ],
opts: _args[ 1 ],
fn: _args[ 2 ]
};
handleBadOptionsForEachHook(obj.opts, zuite);
return 'there is more code but I omitted it';
};
你可以看到第一个方法调用第二个方法,或者第二个方法可以直接调用,两者都是公共API。
我们需要在两个调用中解析参数,但作为优化,如果第二个方法被第一个方法调用而不是直接调用,我们不应该再次解析它们。
我将暂时使用的解决方案是:
beforeEach.cb = function (desc, opts, fn) {
const _args = pragmatik.parse(arguments, rules.hookSignature);
_args[ 1 ].cb = true;
_args[ 1 ].__preParsed = true;
return beforeEach.apply(ctx, _args);
};
opts选项对象是公共的,但用户不会知道__preParsed属性。内部API将。
这个问题是用户可以在没有选项对象的情况下直接调用公共API,并且因为签名非常多,所以我真的不知道,直到我用我的解析引擎解析它,arg如果有对象就是对象!
答案 0 :(得分:2)
您可以通过使用this
调用函数来滥用Function.prototype.call
对象来携带非参数元数据:
function inner (arg1, arg2) {
console.log('inner called with', arg1, arg2)
console.log('inner metadata', this._meta_count)
}
inner.call({_meta_count: 17}, 'ARG ONE', 'ARG TWO')
inner.call({_meta_count: 18}, 'ARG ONE B', 'ARG TWO B')
答案 1 :(得分:1)
您可以在结尾添加一个新的未记录的参数。 JavaScript不关心,以前的调用仍然有效,为什么这对你来说是一个问题?
如果您正在检查参数计数和抛出错误,您可以预期隐藏参数是具有魔术属性的对象,如果不是,则抛出错误。
function go(a, b, c, _internal) {
if (_internal && ! _internal.hasOwnProperty('_magic')) {
throw new Error('invalid internal parameter passed');
}
}
你可以得到更多的偏执并将魔法属性存储为Symbol,然后调用者无法意外通过它,他们必须采取恶意行为。
function go(a, b, c, _internal) {
if (_internal && !_internal.hasOwnProperty(go.internalParamProp)) {
throw new Error('invalid internal parameter passed');
}
console.log("Internal param", _internal && _internal[go.internalParamProp])
}
// Symbol for the magic property name to avoid accidental passing of internal param
go.internalParamProp = Symbol('');
// Passing the internal param
// Uses JS syntax that is not yet supported in some browsers
// If it's a concern, use or var obj={}; obj[go.internalParamProp] = 45
go(1, 2, 3, {
[go.internalParamProp]: 45
})
// Regular call
go(1, 2, 3)
// Invalid call
go(1, 2, 3, 4)