Возвращаемся к истинному сабжу. После обдумывания о концепции чистого виртуального класса а-ля C++, я пришёл к выводу, что на языке JavaScript такую концепцию не имеет смысла реализовывать, по крайней мере в таком виде.
Ещё раз повторю, что подразумевается под чистым виртуальным классом (ЧВК). ЧВК в C++ - это по сути реализация концепция ООП-интерфейсов. То есть мы объявляем ЧВК только в качестве описание для дальнейшей реализации только в наследуемых классах.
Например, создадим ЧВК IODevice и производные от него классы Device1 и Device2.
//C++ code:
#include <stdio.h>
#include <string>
class IODevice
{
protected:
std::string descriptor;
public:
virtual int open() = 0;
virtual ~IODevice() {}
};
class Device1 : public IODevice
{
public:
Device1(std::string _descriptor) //constructor
{
descriptor = _descriptor;
}
int open()
{
printf("%s opening...\n", descriptor.c_str());
return 0;
}
~Device1() {}
};
class Device2 : public IODevice
{
public:
Device2(std::string _descriptor) //constructor
{
descriptor = _descriptor;
}
int open()
{
printf("%s opening...\n", descriptor.c_str());
return 0;
}
};
int main()
{
Device1* dev1 = new Device1("Device1");
Device2* dev2 = new Device2("Device2");
//IODevice* dev = new IODevice(); //error: cannot allocate an object of abstract type 'IODevice'
dev1->open();
dev2->open();
delete dev2;
delete dev1;
return 0;
}
Можно
запустить пример и убедиться, что для каждого класса запускается своя версия метода.
Возвращаюсь к JavaScript. Ведь нет смысла в том, чтобы создавать объект ("ЧВК"), только для того, чтобы просто иметь там свойства, которые по сути и не будут использоваться - это только расход памяти. Я вижу такую реализацию на JS:
"use strict";
Object.defineProperty(Object.prototype, "extends",
{
value: function(parent)
{
this.prototype = Object.create(parent.prototype,
{
constructor:
{
value: this,
writable: true,
configurable: true,
enumerable: false
}
});
arguments[1] === "INTERFACE" ? this.__interfaced__ = true : undefined;
},
enumerable: false,
writable: true,
configurable: true
});
function IODevice(fd)
{
if(this.constructor.hasOwnProperty("__interfaced__"))
{
this._descriptor = fd;
}
else
{
alert("Warning: cannot allocate an object of abstract type " + "\"" + this.constructor.name + "\"");
return new Error("Cannot allocate an object of abstract type " + "\"" + this.constructor.name + "\"");
}
}
function Device1(fd)
{
IODevice.call(this, fd);
}
Device1.extends(IODevice, "INTERFACE");
Device1.prototype.open = function()
{
console.log(this._descriptor + " opening...");
};
function Device2(fd)
{
IODevice.call(this, fd);
}
Device2.extends(IODevice, "INTERFACE");
Device2.prototype.open = function()
{
console.log(this._descriptor + " opening...");
};
var dev1 = new Device1("Device1");
var dev2 = new Device2("Device2");
var dev = new IODevice("dev");
dev1.open();
dev2.open();
console.log(dev);