最近我一直在弄乱Proxy对象,并且我创建了一个非常简单的代理,可以建立一个永无止境的对象链。它基本上是No operations的链条,是的,我知道这很难卖,但是我用它来重新点燃我的一个旧项目,它可以像Optional chaining (koude0)一样起作用(很小),所以我想我会分享。
执行
这是一个很小的代理,带有 [[ProxyHandler]] 捕获[[Get]]和[[Call]],它只是在每个陷阱中返回代理对象本身:
const neverendingObject = new Proxy(function () { }, {
apply: function () { return neverendingObject; },
get: function () { return neverendingObject; }
});
注意 [[[ProxyTarget]] 是一个函数,这将允许neverendingObject()
函数调用而无需任何额外的链接。
出于教育目的,我将在陷阱中添加登录,这样就更容易获得发生的事情:
const neverendingObject = new Proxy(function () { }, {
apply: function (target) { console.log('[[Call]] ()'); return neverendingObject; },
get: function (target, prop) { console.log('[[Get]]', prop); return neverendingObject; }
});
neverendingObject.aProperty.aFunction().aProperty
// [[Get]] aProperty
// [[Get]] aFunction
// [[Call]] ()
// [[Get]] aProperty
怎么运行的
我在apply
零件周围缠绕头时遇到了一些困难,额外的get
呼叫首先获得对aFunction
的引用,然后呼叫。
我在下面包含了呼叫链,突出显示了这一路的每个步骤,这样就对我有所帮助(请记住,每个 [[get]] 和 [[call]] em>将返回neverendingObject
对象)。
-
neverendingObject.aProperty
[[get]] afunction neverendingObject.aProperty.aFunction().aProperty
kouude5 [[get]] appoperty
neverendingObject.aProperty.aFunction
[[call]] ()
neverendingObject.aProperty.aFunction()
[get]]
希望这不会增加任何混乱。
可选的链接(?.
)盒
neverendingObject
无法替换?.
操作员,但是可以在不测试每个步骤的情况下模仿链接的行为。但是,只有最后一个动作是一个函数,是的,这是大多数用例的表演者。
const person = {
arms(hasNoArms) {
if (hasNoArms === true) {
return neverendingObject
}
return {
wave() { console.log('Waving arms'); }
};
}
}
person.arms().wave(); // "Waving arms"
person.arms(true).wave(); // Nothing happens
使用?.
运算符,它看起来像以下内容(假设返回null
而不是neverendingObject
,当设置了hasNoArms
参数时):
person.arms(true)?.wave(); // Nothing happens
?.
操作员更加明确,这将是大多数情况下选择的首选方法。作为奖励,?.
操作员停止执行链的其余部分,其中neverendingObject
将需要将链条运行到尽头,包括任何昂贵的计算作为参数。
实际用例
有了一切,neverendingObject
是错误的,我发现了一个用例,它在现有对象中添加了有条件的链条。
一个非常简单的情况是将and
函数添加到console
对象中,仅当条件是true时,就可以轻松地进行记录输出,而没有添加的情况。
console.and = function (condition) {
if (condition) {
return console;
}
return neverendingObject;
}
console.and(1 == 1).log('is logged'); // "is logged"
console.and(1 == 2).log('is logged'); // Nothing happens
结论
让我们将简单的if
与and()
函数进行比较:
if (isDebugging) {
console.log('Some message');
}
// vs
console.and(isDebugging).log('some message');
它节省了几行,并且(主观上)易于阅读。当使用time()
和timeEnd()
进行一些灯光分析时,这几行加起来:
if (isDebugging) {
console.time('abcd');
}
// Expensive stuff
if (isDebugging) {
console.timeEnd('abcd');
}
// vs
console.and(isDebugging).time('abcd');
// Expensive stuff
console.and(isDebugging).timeEnd('abcd');
我已经开始了一个旧项目的新分支:ConditionalConsole。
以前的版本需要创建另一个对象,而不是console
。使用此技术,我可以装饰它并使功能具有更本地的感觉。