【问题标题】:Defining an interface in JavaScript via prototype chain通过原型链在 JavaScript 中定义接口
【发布时间】:2015-10-01 02:26:06
【问题描述】:

我知道在 C++ 等多种语言中,您可以创建具有多重继承的类(或者至少使用 Java 中的接口模拟它)。在 JavaScript 中,是否可以定义一个可以在类上实现的接口?如果是这样,最好的方法是什么,最好以某种方式合并原型链。下面会起作用吗,还是有更好的方法?

function Gizmo() {
    console.log('Gizmo constructed');
}

Gizmo.prototype.wamboozle = function () {
    console.log('wamboozle');
};

function EventEmitter() {
    console.log('EventEmitter constructed');
    this.events = {};
}

EventEmitter.prototype.on = function (name, callback) {
    this.events[name] ? this.events[name].push(callback) : (this.events[name] = [callback]);
};

EventEmitter.prototype.emit = function (name, event) {
    if (this.events[name]) {
        this.events[name].forEach(function (callback) {
            callback(event);
        });
    }
};

// set up inheritance and implementation

// maybe this could be a possibility?
Doohickey.prototype = Object.create(Gizmo.prototype);

Object.getOwnPropertyNames(EventEmitter.prototype).forEach(function (member) {
    Doohickey.prototype[member] = EventEmitter.prototype[member];
});

function Doohickey() {
    console.log('Doohickey constructed');
    Gizmo.call(this); // initialize base class
    EventEmitter.call(this); // initialize interface
}

Doohickey.prototype.turlywoops = function () {
    console.log('turlywoops');
};

var myOwnDoohickey = new Doohickey();

// member function works
myOwnDoohickey.turlywoops();

// inherited member function works
myOwnDoohickey.wamboozle();

// interface member functions work
myOwnDoohickey.on('finagle', function (trick) {
    console.log(trick);
});

myOwnDoohickey.emit('finagle', {
    hello: 'world!'
});

// both true
console.log(myOwnDoohickey instanceof Doohickey);
console.log(myOwnDoohickey instanceof Gizmo);

// don't mind if this isn't necessarily true, though it would be nice
console.log(myOwnDoohickey instanceof EventEmitter);

【问题讨论】:

  • 不行,不能使用原型链进行多重继承。
  • 请注意foo = Object.create(Foo.prototype);foo instanceof Foo; // true vs foo = Object.create(Foo);foo instanceof Foo; // false
  • @PaulS。已修复,谢谢。

标签: javascript interface prototype multiple-inheritance


【解决方案1】:

使用 underscore.js 之类的东西,您可以基于两个不相关的原型创建一个新对象并将其用作原型。在此示例中,obj2 中定义的属性/方法将覆盖结果对象中 obj1 中的任何属性/方法。

function IAmMulti(){

}
IAmMulti.prototype=_.extend(_.clone(Obj1.prototype),_.clone(Obj2.prototype));

这是一个例子:JavaScript inheritance with _.extend()

【讨论】:

  • 这似乎是合理的。但是在这种情况下,我猜IAmMulti 的实例不会是Obj1Obj2 的实例,对吗?我不是在批评你的回答,我只是确保我更好地理解其中的含义。
  • 对。它只是新类型的一个实例。
  • @DarthDerrr 抱歉,但是将一个对象的原型扩展到另一个对象与面向对象编程中的接口概念有什么关系?接口是对象必须为 T 类型的所有方法的描述。当 duck typing 中没有实现要求时,您如何可能且完全确保 javascript 对象实现一个接口以像 T 类型的对象一样工作?跨度>
  • 我想这真的取决于用例。如果一个人只是想确保一个对象原型具有来自两个不同原型的所有内容,而不需要检查接口的成员资格,那么我提出的建议是可行的。我在我的项目中也做过类似的事情,并且没有遇到任何问题。如果一个对象是新类型的一个实例,我认为它可以工作。
【解决方案2】:

您不能在任何级别继承超过 1 个原型(尽管您可以拥有一堆继承),并且接口没有任何意义,因为 javascript 是一种动态语言,并且会在整个原型链中搜索匹配的名称。没有通过接口访问对象的概念。

【讨论】:

  • 我想指出,接口在 JavaScript 中确实有意义,尤其是在本机级别的实现中。例如在this page of MDN、“Element 上继承其父级Node 及其父级EventTarget 的属性,并实现ParentNodeChildNodeNonDocumentTypeChildNodeAnimatable 的属性。 "那是 4 个不同的接口,所以它在 J​​avaScript 中确实有意义,即使你说它不能通过原型链完成。
  • @PatrickRoberts - 首先,这些都是 DOM 对象,而不是 JavaScript。其次,您不能通过接口访问 javascript 对象。您要么有权访问(“地址”)对象,并且它“完全暴露”,要么您没有
  • 我的问题中的代码有什么问题来创建类似于接口的东西,即使它不一定是它的实例?
【解决方案3】:

JavaScript 中,您最多可以定义一个 Object,它会被您的 Object 继承。 对象的继承也被你的对象等继承,形成一个长链。

function Foo() {
    this.fooThisProp = 'tfoo';
}
Foo.prototype = Object.create(null);
Foo.prototype.fooProtoProp = 'pfoo';

function Bar() {
    Foo.call(this);
    this.barThisProp = 'tbar';
}
Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.barProtoProp = 'pbar';

function Baz() {
    Bar.call(this);
    this.bazThisProp = 'tbaz';
}
Baz.prototype = Object.create(Bar.prototype);
Baz.prototype.bazProtoProp = 'pbaz';

var obj = new Baz();
obj; // {barThisProp: "tbar", bazThisProp: "tbaz", fooThisProp: "tfoo"}
obj.bazProtoProp; // "pbaz"
obj.barProtoProp; // "pbar"
obj.fooProtoProp; // "pfoo"

obj instanceof Baz; // true
obj instanceof Bar; // true
obj instanceof Foo; // true

您始终可以将构造函数应用于不是实例的 Object,因此不会从该构造函数继承。请注意构造函数不假定继承方法等的存在,

var fizz = {};
Bar.call(fizz);
fizz; // {barThisProp: "tbar", fooThisProp: "tfoo"}
fizz.barProtoProp; // undefined
fizz.fooProtoProp; // undefined
fizz instanceof Bar; // false
fizz instanceof Foo; // false

如果你想编写一个方法包装器来做事,那么你可以在任何 context

中使用几乎任何 Object
function Buzz() {}
Buzz.prototype = Object.create(null); // Buzz separate from Foo, Bar, Baz
Buzz.prototype.bazLook = function (key) {
    return Baz.prototype[key]; // or do something else with the value
}

var buzz = new Buzz();
buzz.bazLook('fooProtoProp'); // "pfoo"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-02
    • 2016-04-10
    • 1970-01-01
    • 1970-01-01
    • 2011-12-10
    相关资源
    最近更新 更多