Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Виртуальный класс а-ля Си Плас Плас (https://javascript.ru/forum/misc/51128-virtualnyjj-klass-lya-si-plas-plas.html)

Erolast 25.10.2014 21:16

Ты не понял. Никто не говорит о том, что нужно отказаться от new и использовать Object.create - это, конечно, бред. Object.create надо использовать в функции inherits, ибо нам нужен чистый объект с [[Prototype]], равным прототипу наследуемого класса, без всякой хрени от конструктора.
Цитата:

Мы это могли бы делать и по старинке. {__proto__: example}
Динамическое изменение свойства [[Prototype]] объекта вызовет проблемы с производительностью, ибо это неожиданная неоптимизируемая операция.

Sweet 25.10.2014 21:28

Цитата:

Сообщение от Erolast
Никто не говорит о том, что нужно отказаться от new и использовать Object.create

Я говорю. Это унылый быдлокод, который плодит лишние объекты в цепочки прототипов.

Voronar 25.10.2014 21:33

Цитата:

Сообщение от Erolast (Сообщение 337528)
И не буду, пример неверен. Надо так:
function inherits(Child, Parent) {
  Child.prototype = Object.create(Parent.prototype);
  Child.prototype.constructor = Child;
}

function Interface(){};
function Class(){};

inherits(Class, Interface);

Interface.prototype.someValue = 1;
Class.prototype.someValue = 2;

alert((new Interface()).someValue);
alert((new Class()).someValue);

Кстати, а нельзя вместо объекта во 2-ом аргументе create, просто написать вот так:
Child.prototype.constructor = Child;

terminator-101 25.10.2014 23:03

Цитата:

Сообщение от Sweet
который плодит лишние объекты в цепочки прототипов.

Ну ка покажи пример таких "лишних" объектов, а то возникает ощущение, что ты них*я не понимаешь о чем ты говоришь.

Aetae 25.10.2014 23:05

Цитата:

Сообщение от Sweet (Сообщение 337584)
Я говорю. Это унылый быдлокод, который плодит лишние объекты в цепочки прототипов.

Ты глупости говориь. Сомотри: если тебе нужно чтобы у ребёнка были свои методы помимо методов родителя - тебе так или иначе не обойтись без объекта эти методы хранящего, так? Так где же "лишние объекты"?

Voronar 25.10.2014 23:13

Кстати, ничего страшного не будет, если я стандартному объекту добавлю метод, чтобы наследование получилось более изящным с точки зрения синтаксиса:
Object.prototype.extends = function(parent)
{
  this.prototype = Object.create(parent);
  this.prototype.constructor = this;
};

var Interface =
{
  fd : "default descriptor"
};

function Class1() {};
Class1.extends(Interface);

function Class2() {};
Class2.extends(Interface);

Class1.prototype.fd = "class1 descriptor";

var obj1 = new Class1();
var obj2 = new Class2();
//var obj = new Interface(); //ошибка, так как нельзя создать экземпляр виртуального класса

alert(obj1.fd);
alert(obj2.fd);

Aetae 25.10.2014 23:24

Voronar, только не таким образом, ибо:
Object.prototype.extends = function(){ alert('extends') };
var newObj = {foo: 'bar'};
for(var key in newObj) alert(key);
это может поломать чужой код.

Надо примерно так:
Object.defineProperty(Object.prototype, 'extends', {
  value: function(){},
  enumerable: *!*false*/!*,
  writable: true,
  configurable: true
});

Voronar 25.10.2014 23:44

Aetae
Спасибо, и вправду классная вещь этот Object.defineProperty().

terminator-101 25.10.2014 23:53

Цитата:

Сообщение от Aetae
это может поломать чужой код.

По-идее, эта мантра проповедуется сейчас массово, но проблема высосана из пальца.
Object.prototype.extends = function(){ alert('extends') };
var newObj = {foo: 'bar'};
for(var key in newObj) {if(!(newObj.hasOwnProperty(key))) break; alert(key)};

Тот кто итерирует, должен понимать, что и зачем он итерирует. А проблемы дебила шерифа не е*ут.

terminator-101 25.10.2014 23:57

Voronar,
Если нужна поддержка IE-8, забудь, если я не ошибаюсь, не поддерживает он эту хрень, недавно тема тут была.

terminator-101 26.10.2014 00:12

Voronar,
Вообще, ящетаю, "Безопасное" расширение нативных объектов, могло бы выглядеть вот так
Object.prototype._=function(f){return f(this)}

show=function(x){console.log(x)}
sum=function(x){console.log(x+x)}

;({a: 1})._(show) // { a: 1 }
;1.._(sum) // 2

Расширяем одной функцией, а затем через каллбэк дергаем как хочем. Одну ф-цию задокументировать и запомнить -- не проблема. И не надо никаких сраных костылей enumerable и пр.

Aetae 26.10.2014 00:13

Цитата:

Сообщение от terminator-101 (Сообщение 337600)
но проблема высосана из пальца.

Говорить то легко, а на деле проблемы будут не у говнокодера не задумывающегося о проверках - а лично у тебя, когда срочно потребуется подключить чей-то код и придётся рыться в чужом дерьме.

Voronar, слушать terminator-101 и kostyanet(скорее всего это вообще одно лицо) следует с осторожностью.(обрати внимание на карму) Они живут в своём маленьком трололо мире, слабо связанном с действительностью. Иногда чего дельного и могут сказать в моменты просветления, но в целом..

Erolast 26.10.2014 06:53

Цитата:

Кстати, а нельзя вместо объекта во 2-ом аргументе create, просто написать вот так:
Child.prototype.constructor = Child;
Нет. Конструктор не должен быть enumerable (поломает код с for in).

Цитата:

Я говорю. Это унылый быдлокод, который плодит лишние объекты в цепочки прототипов.
Справедливо, если тебе не требуются собственные методы в наследующем классе (а ТС требуется). Специально для этого в ES6 предусмотрен такой синтаксис:
class Parent {
  someMethod();
}
class Child prototype Parent.prototype {};

console.log((new Child).__proto__ == Parent.prototype); //true

Erolast 26.10.2014 09:51

Цитата:

Сообщение от Aetae (Сообщение 337597)
Надо примерно так:
Object.defineProperty(Object.prototype, 'extends', {
  value: function(){},
  enumerable: *!*false*/!*,
  writable: true,
  configurable: true
});

И не в прототип объекта, а в прототип функции.

terminator-101 26.10.2014 10:20

Цитата:

Сообщение от Erolast
а в прототип функции

А Object -- это что не функция? Или ты имеешь в виду Object.__proto__?
По-идее, то что он написал, это тоже самое, что ({}).__proto__. Вроде, именно так и расширяются нативные объекты. А в саму то функцию что толку писать?
alert(Object.prototype===({}.__proto__)) // true

terminator-101 26.10.2014 10:30

Erolast,
Object.__proto__.one=1
Object.prototype.ten=10
alert([{}.one, {}.ten])

Erolast 26.10.2014 14:26

Цитата:

А Object -- это что не функция?
В прототип класса функции - так яснее?
Object.defineProperty(Function.prototype, "extends", {
  writable: true,
  configurable: true,
  enumerable: false,
  value: function(){}
});

Цитата:

Object.__proto__.one=1
Object.prototype.ten=10
alert([{}.one, {}.ten])
Да ну?

terminator-101 26.10.2014 15:04

Цитата:

Сообщение от Erolast (Сообщение 337650)
В прототип класса функции - так яснее?

То что ты написал, эквивалентно
Function.prototype.extends=function(){}
console.log(function(){}.extends, ({}).extends, "foo".extends)
// [Function] undefined undefined

за исключением сраного enumerable
Ты расширил функцию, а речь шла за объекты.

Цитата:

Да ну?
Что ну? Ликбез тебе провести? Я смотрю, ты ваще конкретно плаваешь в сабже. Пытаешься спрятать свою некомпетентность в нарочито переусложненном коде, и терминологии, придуманной тобой же.

terminator-101 26.10.2014 15:08

Цитата:

Сообщение от Erolast
класса функции - так яснее?

И какие нарен классы. Нет в JS классов никаких. То что подразумевается под классом, это ссылка на конструктор, епт.

Erolast 26.10.2014 16:16

Цитата:

Сообщение от terminator-101 (Сообщение 337538)
Erolast,
Че то я не догоняю, до конца, че там происходит.

Вот пока не догоняешь, помалкивал бы :D

terminator-101 26.10.2014 16:23

Цитата:

Сообщение от Erolast
Вот пока не догоняешь, помалкивал бы

Неохота мне щас парится, есть дела. Позже разберу. Не исключено, что там лажа, подозрительно выглядит. Но то что я сказал выше -- все правильно. Да ты и сам, понял, что обосрался. Аргументация "сам такой" -- это сильно, да. Поздравляю со сливом.

Erolast 26.10.2014 16:25

:D
"Я нихера не понимаю, сейчас-то просто занят, да и вообще там лажа, да и вообще ты слил."

kobezzza 26.10.2014 16:29

Erolast, забей ты на этого нуба :)

terminator-101 26.10.2014 16:31

Erolast,
Я тебе не за то говорил, а за последующие твои лажовые утверждения по-поводу расширения объекта. А к тому коду мы еще вернемся, не переживай.

Erolast 26.10.2014 16:32

Цитата:

Сообщение от kobezzza (Сообщение 337664)
Erolast, забей ты на этого нуба :)

Да я, в общем-то, чисто на терминатора лишь два последних сообщения истратил, остальные были для ТС :)

Voronar 04.11.2014 12:18

Возвращаемся к истинному сабжу. После обдумывания о концепции чистого виртуального класса а-ля 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);

Erolast 04.11.2014 13:49

Цитата:

Object.defineProperty(Object.prototype, "extends",
Определяй не для Object.prototype, а для Function.prototype. Этот метод применим только к функциям.
Задачу можно решить наследованием от объекта, а не от класса:
Object.defineProperty(Function.prototype, "extends", {
    value: function(parent) {
        *!*var inheritFrom = typeof parent == "function" ? parent.prototype : parent;*/!*
        this.prototype = Object.create(inheritFrom, {
            constructor: {
                value: this,
                writable: true,
                configurable: true,
                enumerable: false
            }
        });
    },
    enumerable: false,
    writable: true,
    configurable: true
});

var IODevice = {

};

function Device1() {}
Device1.extends(IODevice);

function Device2() {}
Device2.extends(IODevice);

new Device1();
new Device2();
new IODevice(); //TypeError: IODevice is not a constructor

В ES6 так:
var IODevice = {};
class Device1 prototype IODevice {

}
class Device2 prototype IODevice {

}

Voronar 04.11.2014 14:42

А как в этом случае использовать конструктор абстрактного класса, если теперь это просто объект?

Ещё такой вопрос. Если мы передаём Object.create в качестве аргумента(proto) parent.prototype, то в этом случае реализуется наследование от функции-конструктора, а если просто объект parent, то в это случае мы получаем копию parent?

Erolast 04.11.2014 15:17

Цитата:

А как в этом случае использовать конструктор абстрактного класса, если теперь это просто объект?
Никак, нету никакого конструктора.
Цитата:

Ещё такой вопрос. Если мы передаём Object.create в качестве аргумента(proto) parent.prototype, то в этом случае реализуется наследование от функции-конструктора, а если просто объект parent, то в это случае мы получаем копию parent?
Object.create(otherObj) создает объект с внутренним свойством [[Prototype]] (доступным по геттеру __proto__), установленным на otherObj. При поиске свойства в объекте оно сначала ищется в самом объекте, затем в объекте, на который ссылается его внутреннее свойство [[Prototype]], затем в прототипе того объекта и так далее по цепочке, пока очередной прототип не окажется равным null.

Voronar 04.11.2014 16:17

Цитата:

Сообщение от Erolast;
Никак, нету никакого конструктора.

Тогда твой вариант не подходит.


Цитата:

Сообщение от Erolast;
Object.create(otherObj) создает объект с внутренним свойством [[Prototype]] (доступным по геттеру __proto__), установленным на otherObj.

То есть в случае передачи parent.prototype свойства будут перебираться в прототипе функции-конструктора, а в случае передачи parent непосредственно в объекте функции-конструктора.

Erolast 04.11.2014 16:34

Цитата:

Тогда твой вариант не подходит.
Вообще не стоит пытаться проециривовать один язык на другой. Языки разные и задачи решаются по-разному, аналога ты не напишешь.
Цитата:

То есть в случае передачи parent.prototype свойства будут перебираться в прототипе функции-конструктора, а в случае передачи parent непосредственно в объекте функции-конструктора.
Ответить с цитированием
Свойства будут перебираться в том объекте, ссылка на который передана.
Тут важно отличать свойства [[Prototype]] и prototype. Свойство [[Prototype]] скрытое, есть у любой переменной (в том числе у примитивов) и определяет, в каком объекте будет искаться свойство, если не найдено в текущем. Доступ к нему можно получить либо с помощью геттера __proto__ (устаревший вариант), либо с помощью Object.getPrototypeOf. Свойство prototype есть у функций, оно ссылается на объект, ссылка на который будет записана в свойство [[Prototype]] объекта, сконструированного этой функцией.

kobezzza 04.11.2014 16:35

http://lljs.org/

Erolast 04.11.2014 17:05

Извращенцы :)

tsigel 05.11.2014 09:00

Voronar,
Если очень нужны интерфейсы и привычка к строгой типизации можно посмотреть в сторону TypeScript

Voronar 05.11.2014 13:57

А что перспективнее с точки зрения написания многофункциональных веб-приложений (высокопроизводительных) я-ля desktop?
1)JavaScript (+ES6, asm.js, etc.)
2)TypeScript
3)Dart

Aetae 05.11.2014 14:00

Voronar, смешной вопрос. Да хоть brainfuck если он вам удобен, понятен и приятен в работе.


Часовой пояс GMT +3, время: 14:36.