在Ruby中,我认为您可以调用未定义的方法,同时捕获调用的方法名称,并在运行时处理此方法。
JavaScript能做同样的事情吗?
在Ruby中,我认为您可以调用未定义的方法,同时捕获调用的方法名称,并在运行时处理此方法。
JavaScript能做同样的事情吗?
method_missing 并不适用于 JavaScript,其原因与 Python 相同:在这两种语言中,方法只是函数的属性;而且对象通常具有不可调用的公共属性。相比之下,Ruby 的对象公共接口完全由方法组成。
JavaScript 需要的是一个钩子来捕获对丢失属性的访问,无论它们是方法还是其他类型的属性。Python 已经实现了这一点:可以参考特殊方法 __getattr__。
Mozilla 提出的 __noSuchMethod__ 提案,在这种充满不一致性的语言中引入了另一个不一致性。
JavaScript 的未来是 Proxy 机制(也在 ECMAscript Harmony 中),它更接近 Python 协议的 自定义属性访问,而不是 Ruby 的 method_missing。
f=obj.m;f(x)
等同于 obj.m(x)
。但在 JavaScript 中, obj.m(x)
会将 this
设置为 obj
,而 f=obj.m;f(x)
则不会设置。 - ehabkost__missing__
方法。 - aliqandil__missing__
方法仅用于映射,以添加逻辑来处理映射中缺少键的情况。例如,实现一个使用字符串键但不区分大小写的映射非常有用。它与 method_missing
没有任何关系。 - Luciano Ramalhovar 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]
在替换myObj
的位置使用p
。
要小心使用get
,因为它会截取p
的所有属性请求。因此,p.specialPants()
会导致错误,因为specialPants
返回一个字符串而不是函数。
unknownMethod
实际上等价于以下内容:
var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');
这是因为在JavaScript中,函数也是对象。
如果您知道预期的参数数量,可以像正常声明一样在返回的函数中声明它们。
例如:
...
get: function (target, name) {
return function(expectedArg1, expectedArg2) {
...
目前还没有这个功能。有一个名为代理的ECMAScript Harmony提案,实现了类似的(实际上更加强大的)功能,但是ECMAScript Harmony还没有发布,可能需要几年时间。
let
和const
、简洁的函数语法以及Proxies。 - Jörg W Mittagmethod_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
不,JavaScript没有直接类似于Ruby的method_missing钩子的元编程能力。解释器只是引发一个错误,被调用的代码可以捕获,但无法被访问的对象无法检测到。这里有一些关于在运行时定义函数的答案,但那不是同一件事情。您可以进行大量的元编程,更改对象的特定实例,定义函数,执行像备忘和装饰器之类的功能性操作。但是没有动态元编程缺失功能,就像Ruby或Python中那样。
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
的代码(例如,您可以使用它来包装库对象)。它也适用于使用函数式/闭包风格编写的内容 - 我只是用this /构造函数说明它可以与这样的内容一起使用。
以下是修改:
//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
,但似乎并不必要。
据我所知没有,但是您可以通过将函数初始化为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" */
这个技巧类似于C#中实现递归lambda的技巧,如此处所述。
唯一的缺点是,如果在定义之前使用foo
,你将会得到一个错误,因为尝试调用null
作为函数,而不是更详细的错误消息。但是你期望在定义之前使用函数会得到某些错误消息。