我实际上是在打字稿中处理这个,但我会用Javascript写这个问题。两者的答案都很棒。
我有两个基本上具有相同界面的对象。我想创建一个具有相同接口的第三个对象,每个函数只调用contains中两个对象的方法。有没有办法以编程方式创建所有功能,所以我不必全部输入它们?
c = console.log; // shorthand
obj1 = function() {}
obj1.prototype = {
foo: (x) => { c("1" + x) },
bar: (x, y) => { c("1" + x + y) }
}
obj2 = function() {}
obj2.prototype = {
foo: (x) => { c("2" + x) },
bar: (x, y) => { c("2" + x + y) }
}
obj3 = function() {
this.o1 = new obj1()
this.o2 = new obj2()
}
obj3.prototype = {
foo: function(x) {
this.o1.foo(x);
this.o2.foo(x);
},
bar: function(x, y) {
this.o1.bar(x, y);
this.o2.bar(x, y);
}
}
我正在寻找一种方法来编写obj3,而不必手动写出每个成员函数,因为我有很多成员函数。
答案 0 :(得分:2)
您可以使用Proxy
对象:
function multiproxy(objects) {
return new Proxy(objects[0], {
get: function(target, name) {
var that = this;
return function() {
var result;
for (let obj of objects) {
result = obj[name].apply(that, arguments);
}
return result;
};
}
});
}
所以在你的情况下,你会这样做:
var instance3 = multiproxy([new obj1(), new obj2()]);
现在,instance3
的行为与obj1
相似,但在obj2
上调用相同的方法。
这不会处理对象的非功能属性,但添加起来并不困难。
答案 1 :(得分:0)
您需要复制以下对象:
// Shallow copy
this.o1 = jQuery.extend({}, obj1);
或
// Deep copy
this.o1 = jQuery.extend(true, {}, obj1);
Difference between shallow copy and deep copy
或者,纯JavaScript版本:
this.o1 = Object.assign({}, obj1);
您需要阅读Object.assign()来研究细节。如果你没有进行深层复制而是浅层复制对象,可能会产生意想不到的影响。
经过多次实验后,我意识到obj3
的构造函数函数无法创建新对象,所以我决定移出克隆部分。根据我的观察,这段代码按预期工作:
c = console.log; // shorthand
obj1 = function() {}
obj1.prototype = {
foo: (x) => { c("1" + x) },
bar: (x, y) => { c("1" + x + y) }
}
obj2 = function() {}
obj2.prototype = {
foo: (x) => { c("2" + x) },
bar: (x, y) => { c("2" + x + y) }
}
obj3 = function() {
this.o1;
this.o2;
}
obj3.prototype = {
foo: (x) => {
obj3.o1.foo(x);
obj3.o2.foo(x);
},
bar: (x, y) => {
obj3.o1.bar(x, y);
obj3.o2.bar(x, y);
}
}
obj3.o1 = $.extend(true, {}, obj1.prototype); // jQuery or
obj3.o2 = Object.assign({}, obj2.prototype); // Pure JavaScript
obj3.prototype.foo(5);
obj3.prototype.bar(3,4);
答案 2 :(得分:0)
您可以使用字典语法访问方法:
var a = "Hello world";
a.toUpperString(); // returns "HELLO WORLD"
a["toUpperString"](); // same as above
所以一般方法是先实现一个较低级别的方法:
假设您有对象a
,其中包含对b
和c
的引用
function callMethod(a, methodName) {
// You might want to add some error checking
a.b[methodName]()
a.c[methodName]()
}
现在,您可以致电callMethod(a, "something")
,然后致电a.b.something()
和a.c.something()
这不是一个完整的答案,但我认为它应该足以让你开始。
由于原型本身就是简单的词典,因此您可以轻松地在一个对象的原型中列出方法,并将方法添加到另一个对象的原型中。
答案 3 :(得分:0)
Blender展示了如何通过代理服务器完成这项工作,但是我有兴趣在没有大量额外工作/投射的情况下获得可用的打字。
这是一个以几乎相同的方式执行的解决方案,可以避免在IE / iOS 9中支持代理问题。
function ClassMerger<T extends Object>(...classes: T[]): Partial<T> {
// Helper to create a function that calls a method on all passed instances
let call = (method: keyof T) => (...args: any[]) => {
for (let instance of classes) {
if (typeof instance[method] == 'function') {
// Typescript's type checking fails here for some reason.
(instance[method] as Function).apply(instance, args);
}
}
}
let merged: Partial<T> = {};
// Loop through each class, adding all methods to the merged object
for (let instance of classes) {
for (let method in instance) {
if (typeof instance[method] == 'function' && !merged[method]) {
merged[method] = call(method);
}
}
}
return merged;
}
// Usage (with your example objects):
let merged = ClassMerger<{foo: (a: string) => void}>(new obj1(), new obj2());
if (merged.foo) {
merged.foo('Test'); // No complaints
}
// Property 'bar' does not exist on type 'Partial<{foo: (a: string) => void}>'.
merged.bar('A', 'B');
注意:
该函数返回Partial<T>
,因为如果您的界面中有属性,它们将不会添加到合并对象中。如果您的接口没有任何属性,则可以将返回的对象强制转换为T
的实例。
此方法要求您传递类的实例,而不是为您创建类。这允许一个更简单的方法,并允许您提供容易接受不同构造函数参数的类。