Javascript是否具有类似Ruby的method_missing功能?

我认为在Ruby中,您可以调用一个尚未定义的方法,然后捕获被调用方法的名称,并在运行时对该方法进行处理。

Javascript可以做同样的事情吗?

user310291 asked 2020-02-22T05:17:26Z
8个解决方案
44 votes

由于Python中不存在method_missing,因此method_missing不太适合JavaScript:在两种语言中,方法只是碰巧是函数的属性; 对象通常具有不可调用的公共属性。 与Ruby相比,Ruby的对象的公共接口是100%方法。

JavaScript中需要的是一个钩子,以捕获对丢失属性的访问,无论它们是否为方法。 Python拥有它:请参见__getattr__特殊方法。

Mozilla的__noSuchMethod__提议在与他们混在一起的语言中引入了另一个不一致之处。

JavaScript的前进之路是代理机制(也在ECMAscript Harmony中),与自定义属性访问的Python协议相比,与Ruby的method_missing更接近。

Luciano Ramalho answered 2020-02-22T05:18:26Z
28 votes

您要解释的红宝石功能称为“ method_missing” [http://rubylearning.com/satishtalim/ruby_method_missing.htm。]

这是一项全新功能,仅在某些浏览器(如Firefox)(蜘蛛猴Javascript引擎)中提供。 在SpiderMonkey中,它称为“ __noSuchMethod__” [https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod]

请阅读Yehuda Katz的这篇文章[http://yehudakatz.com/2008/08/18/method_missing-in-javascript/],以了解有关即将实施的更多信息。

lmmendes answered 2020-02-22T05:17:50Z
4 votes

目前不行。 ECMAScript Harmony有一个建议书,叫做proxies,它实现了类似的功能(实际上,功能更强大),但是ECMAScript Harmony尚未发布,并且可能会持续几年。

Jörg W Mittag answered 2020-02-22T05:18:47Z
2 votes

我已经为javascript创建了一个库,可让您在javascript中使用method_missing:[https://github.com/ramadis/unmiss]

它使用ES6代理进行工作。 这是一个使用ES6类继承的示例。 但是,您也可以使用装饰器来达到相同的结果。

import { MethodMissingClass } from 'unmiss'

class Example extends MethodMissingClass {
    methodMissing(name, ...args) {
        console.log(`Method ${name} was called with arguments: ${args.join(' ')}`);
    }
}

const instance = new Example;
instance.what('is', 'this');

> Method what was called with arguments: is this
Rama answered 2020-02-22T05:19:11Z
1 votes

不,在JavaScript中没有直接类似于ruby的method_missing钩子的元编程功能。 解释器仅引发一个错误,调用代码可以捕获该错误,但是无法被正在访问的对象检测到。 这里有一些关于在运行时定义函数的答案,但这不是一回事。 您可以执行许多元编程,更改对象的特定实例,定义函数,执行诸如记忆和修饰符之类的功能。 但是没有像ruby或python中那样缺少函数的动态元编程。

Peter Lyons answered 2020-02-22T05:19:33Z
1 votes

我之所以提出这个问题,是因为我正在寻找一种方法,该方法可以在第一个对象上不存在该方法时使用。 它不像您的要求那么灵活-例如,如果两种方法都缺失,那么它将失败。

我正在考虑为我拥有的一个小库执行此操作,该库有助于以某种方式配置extjs对象,并使它们更具可测试性。 我有单独的调用来实际获取用于交互的对象,并认为这可能是通过有效地返回增强类型将这些调用组合在一起的一种好方法

我可以想到两种方法:

样机

您可以使用原型进行此操作-因为如果原型不在实际对象上,则东西会落入原型。 如果要删除的一组函数使用this关键字,这似乎将不起作用-显然,您的对象不会知道或不在乎另一个人知道的东西。

如果所有代码都是您自己的,并且您没有使用此代码和构造函数...这出于很多原因是个好主意,那么您可以这样做:

    var makeHorse = function () {
        var neigh = "neigh";

        return {
            doTheNoise: function () {
                return neigh + " is all im saying"
            },
            setNeigh: function (newNoise) {
                neigh = newNoise;
            }
        }
    };

    var createSomething = function (fallThrough) {
        var constructor = function () {};
        constructor.prototype = fallThrough;
        var instance = new constructor();

        instance.someMethod = function () {
            console.log("aaaaa");
        };
        instance.callTheOther = function () {
            var theNoise = instance.doTheNoise();
            console.log(theNoise);
        };

        return instance;
    };

    var firstHorse = makeHorse();
    var secondHorse = makeHorse();
    secondHorse.setNeigh("mooo");

    var firstWrapper = createSomething(firstHorse);
    var secondWrapper = createSomething(secondHorse);
    var nothingWrapper = createSomething();

    firstWrapper.someMethod();
    firstWrapper.callTheOther();
    console.log(firstWrapper.doTheNoise());

    secondWrapper.someMethod();
    secondWrapper.callTheOther();
    console.log(secondWrapper.doTheNoise());

    nothingWrapper.someMethod();
    //this call fails as we dont have this method on the fall through object (which is undefined)
    console.log(nothingWrapper.doTheNoise());

这对我的用例不起作用,因为extjs家伙不仅错误地使用了“ this”,他们还基于使用原型和“ this”的原理构建了一个疯狂的经典继承类型系统。

这实际上是我第一次使用原型/构造函数,而我对不能只设置原型感到困惑,还必须使用构造函数。 在对象中(至少在firefox中)有一个魔域,称为__proto,这基本上是真实的原型。 似乎实际的原型字段仅在构建时使用...多么令人困惑!


复制方式

此方法可能更昂贵,但对我来说似乎更优雅,并且也可以在使用bind的代码上工作(例如,因此可以使用它包装库对象)。 它也可以用功能/闭包样式编写的东西上工作-我刚刚用this /构造函数说明了它,以显示它可以用类似的东西工作。

这是mod:

    //this is now a constructor
    var MakeHorse = function () {
        this.neigh = "neigh";
    };

    MakeHorse.prototype.doTheNoise = function () {
        return this.neigh + " is all im saying"
    };
    MakeHorse.prototype.setNeigh = function (newNoise) {
        this.neigh = newNoise;
    };

    var createSomething = function (fallThrough) {
        var instance = {
            someMethod : function () {
                console.log("aaaaa");
            },
            callTheOther : function () {
                //note this has had to change to directly call the fallThrough object
                var theNoise = fallThrough.doTheNoise();
                console.log(theNoise);
            }
        };

        //copy stuff over but not if it already exists
        for (var propertyName in fallThrough)
            if (!instance.hasOwnProperty(propertyName))
                instance[propertyName] = fallThrough[propertyName];

        return instance;
    };

    var firstHorse = new MakeHorse();
    var secondHorse = new MakeHorse();
    secondHorse.setNeigh("mooo");

    var firstWrapper = createSomething(firstHorse);
    var secondWrapper = createSomething(secondHorse);
    var nothingWrapper = createSomething();

    firstWrapper.someMethod();
    firstWrapper.callTheOther();
    console.log(firstWrapper.doTheNoise());

    secondWrapper.someMethod();
    secondWrapper.callTheOther();
    console.log(secondWrapper.doTheNoise());

    nothingWrapper.someMethod();
    //this call fails as we dont have this method on the fall through object (which is undefined)
    console.log(nothingWrapper.doTheNoise());

我实际上预期必须在某处使用bind,但这似乎不是必需的。

JonnyRaa answered 2020-02-22T05:20:44Z
0 votes

您可以使用Proxy类。

var myObj = {
    someAttr: 'foo'
};

var p = new Proxy(myObj, {
    get: function (target, methodOrAttributeName) {
        // target is the first argument passed into new Proxy, aka. target is myObj

        // First give the target a chance to handle it
        if (Object.keys(target).indexOf(methodOrAttributeName) !== -1) {
            return target[methodOrAttributeName];
        }

        // If the target did not have the method/attribute return whatever we want

        // Explicitly handle certain cases
        if (methodOrAttributeName === 'specialPants') {
            return 'trousers';
        }

        // return our generic method_missing function
        return function () {
            // Use the special "arguments" object to access a variable number arguments
            return 'For show, myObj.someAttr="' + target.someAttr + '" and "'
                   + methodOrAttributeName + '" called with: [' 
                   + Array.prototype.slice.call(arguments).join(',') + ']';
        }
    }
});

console.log(p.specialPants);
// outputs: trousers

console.log(p.unknownMethod('hi', 'bye', 'ok'));
// outputs: 
// For show, myObj.someAttr="foo" and "unknownMethod" called with: [hi,bye,ok]

关于

您将使用unknownMethod代替p

您应该对unknownMethod小心,因为它会截获p的所有属性请求。因此p.specialPants()将导致错误,因为specialPants返回的是字符串而不是函数。

unknownMethod的实际操作等效于以下内容:

var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');

之所以可行,是因为函数是javascript中的对象。

奖金

如果知道期望的参数数量,则可以在返回的函数中将其声明为正常参数。
例如:

...
get: function (target, name) {
    return function(expectedArg1, expectedArg2) {
...
Lindsay-Needs-Sleep answered 2020-02-22T05:21:41Z
-1 votes

据我所知,您可以通过首先将函数初始化为foo并随后替换实现来模拟它。

var foo = null;
var bar = function() { alert(foo()); } // Appear to use foo before definition

// ...

foo = function() { return "ABC"; } /* Define the function */
bar(); /* Alert box pops up with "ABC" */

如此处所述,此技巧类似于用于实现递归lambda的C#技巧。

唯一的缺点是,如果在定义之前使用foo,尝试将null当作函数,则会收到错误提示,而不是更具描述性的错误消息。 但是您可能希望在定义函数之前收到一些使用该函数的错误消息。

Adam Mihalcin answered 2020-02-22T05:22:11Z
translate from https://stackoverflow.com:/questions/9779624/does-javascript-have-something-like-rubys-method-missing-feature