Javascript 有类似 Ruby 的 method_missing 功能吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9779624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Does Javascript have something like Ruby's method_missing feature?
提问by user310291
In Ruby I think you can call a method that hasn't been defined and yet capture the name of the method called and do processing of this method at runtime.
在 Ruby 中,我认为您可以调用尚未定义的方法,但仍可以捕获所调用方法的名称,并在运行时处理此方法。
Can Javascript do the same kind of thing ?
Javascript 可以做同样的事情吗?
采纳答案by lmmendes
The ruby feature that you are explaining is called "method_missing" http://rubylearning.com/satishtalim/ruby_method_missing.htm.
您正在解释的 ruby 功能称为“method_missing” http://rubylearning.com/satishtalim/ruby_method_missing.htm。
It's a brand new feature that is present only in some browsers like Firefox (in the spider monkey Javascript engine). In SpiderMonkey it's called "__noSuchMethod__" https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod
这是一个全新的功能,仅存在于某些浏览器中,例如 Firefox(在蜘蛛猴 Javascript 引擎中)。在 SpiderMonkey 中,它被称为“__noSuchMethod__” https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod
Please read this article from Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/for more details about the upcoming implementation.
请阅读 Yehuda Katz 的这篇文章http://yehudakatz.com/2008/08/18/method_missing-in-javascript/,了解有关即将实施的更多详细信息。
回答by Luciano Ramalho
method_missingdoes not fit well with JavaScript for the same reason it does not exist in Python: in both languages, methods are just attributes that happen to be functions; and objects often have public attributes that are not callable. Contrast with Ruby, where the public interface of an object is 100% methods.
method_missing不适合 JavaScript,原因与 Python 中不存在的原因相同:在两种语言中,方法只是碰巧是函数的属性;和对象通常具有不可调用的公共属性。与 Ruby 相比,对象的公共接口是 100% 的方法。
What is needed in JavaScript is a hook to catch access to missing attributes, whether they are methods or not. Python has it: see the __getattr__special method.
JavaScript 中需要的是一个钩子来捕获对缺失属性的访问,无论它们是否是方法。Python 有它:请参阅__getattr__特殊方法。
The __noSuchMethod__proposal by Mozilla introduced yet another inconsistency in a language riddled with them.
Mozilla的__noSuchMethod__提议在充满它们的语言中引入了另一个不一致之处。
The way forward for JavaScript is the Proxy mechanism(also in ECMAscript Harmony), which is closer to the Python protocol for customizing attribute accessthan to Ruby's method_missing.
JavaScript 的前进方向是Proxy 机制(也在ECMAscript Harmony 中),它更接近于自定义属性访问的 Python 协议,而不是 Ruby 的method_missing。
回答by J?rg W Mittag
Not at the moment, no. There is a proposal for ECMAScript Harmony, called proxies, which implements a similar (actually, much more powerful) feature, but ECMAScript Harmony isn't out yet and probably won't be for a couple of years.
目前没有,没有。有一个关于 ECMAScript Harmony 的提议,称为proxies,它实现了一个类似的(实际上,更强大的)功能,但是 ECMAScript Harmony 还没有出现,并且可能不会在几年内出现。
回答by Rama
I've created a library for javascript that let you use method_missing
in javascript: https://github.com/ramadis/unmiss
我为 javascript 创建了一个库,让您可以method_missing
在 javascript 中使用:https: //github.com/ramadis/unmiss
It uses ES6 Proxies to work. Here is an example using ES6 Class inheritance. However you can also use decorators to achieve the same results.
它使用 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
回答by Lindsay-Needs-Sleep
You can use the Proxyclass.
您可以使用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]
About
关于
You would use p
in place of myObj
.
您将使用p
代替myObj
.
You should be careful with get
because it intercepts all attribute requests of p
. So, p.specialPants()
would result in an error because specialPants
returns a string and not a function.
您应该小心,get
因为它会拦截p
. 因此,p.specialPants()
会导致错误,因为specialPants
返回的是字符串而不是函数。
What's really going on with unknownMethod
is equivalent to the following:
真正发生的事情unknownMethod
等同于以下内容:
var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');
This works because functions are objects in javascript.
这是有效的,因为函数是 javascript 中的对象。
Bonus
奖金
If you know the number of arguments you expect, you can declare them as normal in the returned function.
eg:
如果您知道期望的参数数量,则可以在返回的函数中将它们声明为正常的。
例如:
...
get: function (target, name) {
return function(expectedArg1, expectedArg2) {
...
回答by JonnyRaa
I came to this question because I was looking for a way to fall through to another object if the method wasn't present on the first object. It's not quite as flexible as what your asking - for instance if a method is missing from both then it will fail.
我来到这个问题是因为我正在寻找一种方法,如果第一个对象上不存在该方法,则可以通过另一个对象。它不像您要求的那样灵活 - 例如,如果两者都缺少一个方法,那么它就会失败。
I was thinking of doing this for a little library I've got that helps configure extjs objects in a way that also makes them more testable. I had seperate calls to actually get hold of the objects for interaction and thought this might be a nice way of sticking those calls together by effectively returning an augmented type
我正在考虑为我拥有的一个小库执行此操作,该库有助于以一种也使它们更易于测试的方式配置 extjs 对象。我有单独的调用来实际获取交互对象,并认为这可能是通过有效返回增强类型将这些调用粘在一起的好方法
I can think of two ways of doing this:
我可以想到两种方法来做到这一点:
Prototypes
原型
You can do this using prototypes - as stuff falls through to the prototype if it isn't on the actual object. It seems like this wouldn't work if the set of functions you want drop through to use the this keyword - obviously your object wont know or care about stuff that the other one knows about.
你可以使用原型来做到这一点——因为如果它不在实际对象上,东西就会落入原型。如果您想要删除的一组函数使用 this 关键字,这似乎不起作用 - 显然您的对象不会知道或关心另一个人知道的东西。
If its all your own code and you aren't using this and constructors ... which is a good idea for lots of reasons then you can do it like this:
如果它是您自己的所有代码,并且您没有使用 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());
This doesn't work for my use case as the extjs guys have not only mistakenly used 'this' they've also built a whole crazy classical inheritance type system on the principal of using prototypes and 'this'.
这对我的用例不起作用,因为 extjs 人员不仅错误地使用了“this”,而且还基于使用原型和“this”的原则构建了一个完整的疯狂的经典继承类型系统。
This is actually the first time I've used prototypes/constructors and I was slightly baffled that you can't just set the prototype - you also have to use a constructor. There is a magic field in objects (at least in firefox) call __proto which is basically the real prototype. it seems the actual prototype field is only used at construction time... how confusing!
这实际上是我第一次使用原型/构造函数,我有点困惑,你不能只设置原型——你还必须使用构造函数。对象中有一个魔法域(至少在 Firefox 中)调用 __proto ,它基本上是真正的原型。似乎实际的原型字段仅在构建时使用......多么令人困惑!
Copying methods
复制方法
This method is probably more expensive but seems more elegant to me and will also work on code that is using this
(eg so you can use it to wrap library objects). It will also work on stuff written using the functional/closure style aswell - I've just illustrated it with this/constructors to show it works with stuff like that.
这种方法可能更昂贵,但对我来说似乎更优雅,并且也适用于正在使用的代码this
(例如,您可以使用它来包装库对象)。它也适用于使用函数式/闭包样式编写的东西 - 我刚刚用 this/constructors 说明了它,以表明它适用于类似的东西。
Here's the mods:
这是模组:
//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());
I was actually anticipating having to use bind
in there somewhere but it appears not to be necessary.
我实际上预计必须bind
在某个地方使用,但似乎没有必要。
回答by Peter Lyons
No, there is no metaprogramming capability in javascript directly analogous to ruby's method_missing hook. The interpreter simply raises an Error which the calling code can catch but cannot be detected by the object being accessed. There are some answers here about defining functions at run time, but that's not the same thing. You can do lots of metaprogramming, changing specific instances of objects, defining functions, doing functional things like memoizing and decorators. But there's no dynamic metaprogramming of missing functions as there is in ruby or python.
不,javascript 中没有直接类似于 ruby 的 method_missing 钩子的元编程功能。解释器只是引发一个错误,调用代码可以捕获该错误,但被访问的对象无法检测到该错误。这里有一些关于在运行时定义函数的答案,但这不是一回事。您可以进行大量元编程、更改对象的特定实例、定义函数、执行诸如记忆和装饰器之类的功能性事情。但是没有像 ruby 或 python 那样对缺失函数进行动态元编程。
回答by Adam Mihalcin
Not to my knowledge, but you can simulate it by initializing the function to null
at first and then replacing the implementation later.
据我所知,您可以通过null
首先将函数初始化为,然后稍后替换实现来模拟它。
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" */
This trick is similar to a C# trick for implementing recursive lambdas, as described here.
这招是类似于C#技巧实现递归lambda表达式,如所描述这里。
The only downside is that if you douse foo
before it's defined, you'll get an error for trying to call null
as though it were a function, rather than a more descriptive error message. But you would expect to get someerror message for using a function before it's defined.
唯一的缺点是,如果您在定义之前确实使用了foo
它,那么您将收到一个错误,null
因为它试图像函数一样调用,而不是更具描述性的错误消息。但是在定义函数之前,您可能会期望得到一些使用函数的错误消息。