【问题标题】:Make object or class property only invocable使对象或类属性仅可调用
【发布时间】:2020-01-22 02:18:23
【问题描述】:

TL:DR;是否可以使对象的属性仅可调用(作为函数)?

这是什么意思

class Foo{
  bar(value){
    return value
  }
}

let newFoo = new Foo()

console.log(newFoo.bar(123))  // should work fine as function is invoked
console.log(newFoo.bar)  // here i need to throw or display an error instead of returning value

我尝试使用Proxyhandler.get 陷阱来执行此操作,但我不知道如何捕获它是函数调用还是只是属性访问,

class Foo {
  bar(value) {
    return value
  }
}


const proxied = new Proxy(new Foo(), {
  get: function(target, prop, reciver) {
    if (prop === 'bar') {
      throw new Error('Bar is method need to be invoced')
    }
    return target[prop]
  }
})

console.log(proxied.bar(true))
console.log(proxied.bar)

我也检查了handler.apply,但这似乎也没有用,因为这是功能上的陷阱,而不是属性上的陷阱

class Foo {
  bar(value) {
    return value
  }
}


const proxied = new Proxy(new Foo(), {
  apply: function(target, thisArg, argumentsList) {
    return target(argumentsList[0])
  },
  get: function(target, prop, reciver) {
    if (prop === 'bar') {
      throw new Error('Bar is method need to be invoced')
    }
    return target[prop]
  }
})

console.log(proxied.bar(true))
console.log(proxied.bar)

【问题讨论】:

  • 我不认为所以,newFoo.bar 将在后续参数列表应用于前一个表达式之前被评估为表达式,所以如果访问 newFoo.bar 会抛出, 调用newFoo.bar 也得扔
  • @CertainPerformance 是的,这就是我的想法,因为. 访问器将返回属性,然后将被调用,但不确定,因为在 JS 中总是有实现某件事的方法,所以希望有无论如何都要这样做
  • 为什么需要这样做,听起来像XY problem
  • @JaromandaX 我正在尝试练习代理,并试图模仿严格的环境(或者您可以将其称为对象的 linter 类的东西)只是为了学习目的,这可能是 XY问题我不确定,我厌倦了我对代理的任何知识并且无法成功,所以我在这里问人们是否有任何其他方式可以完成,或者首先可以完成
  • @Code Maniac 因为据我所知你必须在call 之前get,你可以检查它是否在get 之后被调用,所以像这样jsfiddle.net/y52khqs8

标签: javascript object ecmascript-6 es6-proxy


【解决方案1】:

不,这是不可能的。没有区别

const newFoo = new Foo()
newFoo.bar(123);

const newFoo = new Foo()
const bar = newFoo.bar;
Function.prototype.call.call(bar, newFoo, 123); // like `bar.call(newFoo, 123)`
// or Reflect.apply(bar, newFoo, [123]);

newFoobar 都不能“从内部”区分这些。现在在属性访问和方法调用之间可能会发生任意事情,并且在属性访问期间您无法知道接下来会发生什么,因此您不能过早地抛出异常。方法调用可能永远不会发生(在newFoo.bar; 中),并且仅从newFoo 无法识别。

唯一的方法是拦截newFoo 及其属性和throw 上的所有其他访问您检测到恶作剧序列后;可能在整个程序运行后让您的“linter”从外部检查序列:

const lint = {
  access: 0,
  call: 0,
  check() {
    console.log(this.access == this.call
      ? "It's ok"
      : this.access > this.call
        ? "method was not called"
        : "property was reused");
  },
  run(fn) {
    this.call = this.access = 0;
    try {
      fn();
    } finally {
      this.check();
    }
  }
}

function bar(value) {
  lint.call++; lint.check();
  return value;
}
class Foo {
  get bar() {
    lint.check(); lint.access++;
    return bar;
  }
}
lint.run(() => {
  const newFoo = new Foo;
  newFoo.bar(123);
});
lint.run(() => {
  const newFoo = new Foo;
  newFoo.bar;
});
lint.run(() => {
  const newFoo = new Foo;
  const bar = newFoo.bar;
  bar(123);
  bar(456);
});

更好的解决方案可能是为简单的表达式编写自己的解释器,它只允许方法调用。

【讨论】:

  • 感谢您详细的回答队友:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-13
  • 1970-01-01
  • 2019-01-09
  • 2021-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多