如何创建一个可以调用任何方法的JS对象

How can i create a JS Object that I can call any method on?

本文关键字:方法 任何 调用 对象 JS 一个 何创建 创建      更新时间:2023-09-26

我想创建一个javascript对象,我可以对其调用任何方法,而无需定义它们。理想情况下,我可以把它当作一个函数来调用,它会调用我定义的一个函数,该函数的名称作为它的参数。

所以我会用callMethod(methodName)方法定义一个对象,当我调用时

thisObject.doAThing();

它将调用thisObject.callMethod("doAThing");

这在javascript中可能吗?

不,这是不可能的。如果JavaScript对象没有属性,则不能将未定义的值视为方法。

至少在Firefox中,您可以使用神奇的方法__noSuchMethod__来实现您的目标:

var o = {}
o.__noSuchMethod__ = function(id, args) { alert(id + args); }
o.foo(2,3) //will alert "foo" and "2,3"

请注意,这不是标准配置,正在考虑删除,因此不会将其添加到V8中。

原始帖子(对不起,应该在q评论中问这个问题):

我很难理解要点。如果callMethod在某个地方可以访问"doAThing"方法,为什么不能在对象的实例化时或者每当callMethod的源添加了新方法时插入它呢?

不是想纠缠你。只是想看看在调用/应用/原型范式的疯狂世界中,是否有可能以其他方式实现你希望实现的目标。

编辑添加在此评论后:

我想创建一个代理对象,将其调用委托给另一个对象。–msfeldstein

好吧,原型可能就是答案,因为它基本上充当了对象本身没有的方法的后备。每个函数都有一个原型属性,基本上只是一个普通的对象。当函数用作构造函数时,当您调用构造函数实例上没有的属性时,分配给构造函数原型的方法和属性将成为构造函数实例的后备。您可以将属性添加到原型对象中,这些属性将有效地提供给已经创建的实例。因此,在关联对象的情况下,我想这样做:

//A js constructor is just a function you intend to invoke with the 'new' keyword
//use of 'this.property' will make that property public in the instance
//var effectively makes it private
//Constructors funcs differ from classes in that they don't auto-handle inheritance down to other constructors. You have to come up with a prototype merging scheme to do that.
function MyFacadeConstructor(){ //expected args are objects to associate
        var i = arguments.length; //arguments is a collection of args all funcs have
        while(i--){
            var thisObj = arguments[i];
            associateObjMethods(thisObj);
        }
        //makes it public method but allows function hoisting internally
        this.associateObjMethods = associateObjMethods;
        function associateObjMethods(obj){
            for(var x in obj){
                if(obj[x].constructor.name === 'Function'){ //normalize for <= IE8
                    MyFacadeConstructor.prototype[x] = obj[x];
                    //or if we literally want the other method firing in its original context
                    //MyFacadeConstructor.prototype[x] = function(arg){ obj[x](arg); }
                    //Not sure how you would pass same number of arguments dynamically
                    //But I believe it's possible
                    //A workaround would be to always pass/assume one arg
                    //and use object literals when multiple are needed
                }
            }
        }
    }
    function FirstNameAnnouncer(){
      this.alertFirst = function(){
          alert('Erik');
      }
    }
    var fNamer = new FirstNameAnnouncer();
    var newFacade = new MyFacadeConstructor(fNamer);
    //newFacade.alertFirst should work now;
    newFacade.alertFirst();
    //but we can also associate after the fact
    function LastNameAnnouncer(){
        this.alertLast = function(){ alert('Reppen'); }
    }
    var lNamer = new LastNameAnnouncer();
    newFacade.associateObjMethods(lNamer);
    //now newFacade.alertLast should work
    newFacade.alertLast();

现在,如果你想让调用对象的上下文起作用,我推荐一个事件驱动的接口,这是JS非常适合的。如果你正在寻找的facade方法有任何方面我没有在这里实现,请告诉我。

您可以使用Proxy对象拦截任何方法调用。

function proxifyMethodCalls(obj) {
    const handler = {
        get(target, prop, receiver) {
            return function (...args) {
                console.log(`Intercepted '${prop}' with arguments ${JSON.stringify(args)}`);
                target.callMethod(prop);
            };
        }
    };
    return new Proxy(obj, handler);
}
let obj = {
    callMethod: (methodName) => {
        console.log(`'callMethod' called with '${methodName}'`);
    }
};
obj = proxifyMethodCalls(obj);
obj.doAThing(true);

相关文章: