Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Еще один тупой вопрос по наследованию (https://javascript.ru/forum/misc/51888-eshhe-odin-tupojj-vopros-po-nasledovaniyu.html)

kostyanet 24.11.2014 09:23

Еще один тупой вопрос по наследованию
 
В js была каша, по остаточному принципу как всегда заваривается. Пришла пора разобрать все, и оказалось что у меня не хватает тямы это сделать.

В php есть коллекция (объект) ActiveForm в которую собираются объекты полутора классов: FormField - для вообще полей; и FormFile extends FormField - для файловых. В субклассе FormFile входящие данные модели тупо отсылаются в конструктор родителя parent::__constuct($data); Ну и затем свои проперти-методы добавляются и отчасти перезаписывают родительские. Все чики-пуки.

В JS у меня пока что вот так получается:

if(type=='file')
	var obj=FormFile(opts,progress);
else
	var obj=FormField();
				
obj.elem=		elem;
obj.error=		null;
obj.index=		index;
obj.type=		type;
obj.title=		elem.title;
// тут еще куча пропертей


Если я выдам условно родительскому объекту element

var obj=FormField(element);

и там профтыкаю все нормально, то, дальше не пойму что делать. У этих двух объектов кроме перечисленных пропертей - ничего общего.

kostyanet 24.11.2014 09:27

Если сначала сделать безымянный объект

var obj={
elem: elem,
error: error,
//...
}

то как его расширить методами которые как объект-же возвращают перечисленные "классы", то есть это функции

var FormFile = function(opts,progress){
  // внутренности
 return {
  // наружности
 };
};

krutoy 24.11.2014 12:04

kostyanet,
Я нихнепонел, но предположу, что тебе надо что-то типа:
O=function(){}
O.prototype.extend=function(src){
   for(var i in src){this[i]=src[i]}
   return this
}

fu=function(o){
   return function(src){return o.extend(src)}
}(new O)

alert(fu({a: 1}))
alert(fu({b: 2}))
//  { a: 1 }
//  { a: 1, b: 2 }

kostyanet 24.11.2014 12:30

Переписать проперти по-одной я знаю как. Но в php я ничего не переписываю. Выше нарисовано как это происходит - массив с данными передается родителю, а тот кладет их в свою приватную переменную и - добавляю теперь - выдает через геттер по требованию. Если без геттера, то он бы их просто выжал (extract) в свой this и естественно этот this стал бы this для наследника.

Метафора классического наследования в программировании - целлулоид. Например есть абстрактный класс с чертежом. На этот класс кладется целлулоид и по нему рисуются детали, фон, кое-что полностью перерисовывается. Затем на этот целлулоид кладется следующий целлулоид, который заменяет или модифицирует часть, добавляет еще деталей, цвета, формы итд. Пачка целлулоидов формирует картину. В ран-тайм - анимацию.

Так вот жаба-наследование не классическое и метафора у него типа участок электрической цепи с индукцией на свои проперти. Я ее не могу осилить до сих пор.

kostyanet 24.11.2014 12:37

Не приватную - protected, конечно.

Вкратце как на php более конкретно (чертеж)

// в методе коллекции
$data=$model->columns();
foreach($data as $name=>$col) {
  if($col['type']=='file')
    $this->_d[$name]= new FormFile($col,$model->file_options());
  else
    $this->_d[$name]= new FormField($col);
}

// в файлах
class FormField {

  protected $_d=array();

  public function __construct($data){
     $this->_d=$data;
  }

}

class FormFile extends FormField {
  
  private $opts=array();

  public function __construct($data,$opts){
     parent::__construct($data);
     $this->opts=$opts;
  }

}


вопрос - как сделать такое же на js

kostyanet 24.11.2014 12:58

В php главная задача рендерить контент, поэтому из строки массива делается объект, который сам себя рендерит и коллекция сама себя рендерит, в итоге там где хочется просто пишешь echo $this->form; и вся эта тряхомудия в мгновение ока превращается в голимую кучу хтмля.

В js такой задачи у меня нет, но именно долбаное поле file требует рендера и поэтому я подумал что надо просто сделать так же, как в php и будет работать. Оно работает, но через жопу. То есть приватные методы нихера не знают про все эти проперти, потому что они торчат наружу, а как их внутрь завести я еще не понял.

krutoy 24.11.2014 13:16

Цитата:

Сообщение от kostyanet
Метафора классического наследования в программировании - целлулоид. Например есть абстрактный класс с чертежом. На этот класс кладется целлулоид и по нему рисуются детали, фон, кое-что полностью перерисовывается. Затем на этот целлулоид кладется следующий целлулоид, который заменяет или модифицирует часть, добавляет еще деталей, цвета, формы итд. Пачка целлулоидов формирует картину. В ран-тайм - анимацию.

Описанная тобой модель примитивна до безобразия
def=Object.create
o={a: 1}
o1=def(o)
o1.b=2
o2=def(o1)
o2.c=3
alert([o2.a, o2.b, o2.c])
// [ 1, 2, 3 ]

Цитата:

Сообщение от kostyanet
жаба

При чем тут жаба?
Цитата:

Сообщение от kostyanet
типа участок электрической цепи с индукцией на свои проперти

Я че то не понял твоей аналогии. Можешь расшифровать?

По сабжу: PHP не знаю, поэтому код твой не понимаю. Описание задачи -- тоже мутное. Может быть, если бы ты описал задачу максимально абстрактно, я бы мог тебе помочь, если бы смог понять, что ты хочешь.

kostyanet 24.11.2014 15:47

Я не понимаю зачем вы тогда отвечаете если не понимаете.

devote 24.11.2014 16:44

function FormField(data) {
  this._d = data;
}

function FormFile(data, opts) {
  FormField.call(this, data);
  this.opts = opts;
}

FormFile.prototype = FormField.prototype;
FormFile.prototype.constructor = FormFile;

var a = new FormFile(1, 2);
var b = new FormFile(10, 22);
console.log(a, a instanceof FormField, b instanceof FormField);
console.log(b, a instanceof FormFile, b instanceof FormFile);

Erolast 24.11.2014 16:45

Конструктор не должен быть enumerable.

krutoy 24.11.2014 17:07

Erolast,
По-моему, эту строчку можно вообще опустить, она ничего не делает
function FormField(data) {
  this._d = data;
}
 
function FormFile(data, opts) {
  FormField.call(this, data);
  this.opts = opts;
}
 
FormFile.prototype = FormField.prototype;
//FormFile.prototype.constructor = FormFile;
 
var a = new FormFile(1, 2);
var b = new FormFile(10, 22);
console.log(a, a instanceof FormField, b instanceof FormField);
console.log(b, a instanceof FormFile, b instanceof FormFile);

точней, делает то, что перезаписывает скрытую ссылку на явную.

devote 24.11.2014 18:56

Цитата:

Сообщение от Erolast
Конструктор не должен быть enumerable.

а где он таков есть?

Цитата:

Сообщение от krutoy
точней, делает то, что перезаписывает скрытую ссылку на явную.

кто сказал?

где вы тут увидели открытие перечислимости конструктора?
прогоните объекты a или b через for...in и посмотрите где там перечисляется конструктор?

nerv_ 24.11.2014 19:24

Цитата:

Сообщение от devote
где вы тут увидели открытие перечислимости конструктора?
прогоните объекты a или b через for...in и посмотрите где там перечисляется конструктор?

он вот о чем https://github.com/nervgh/yum.js/blo...rc/yum.js#L177

Хотя, по хорошему, на это пофиг.

krutoy 24.11.2014 19:27

devote,
Я не так выразился. Вы явно переопределили ссылку, скрытую, которая там и так была. Это лишнее телодвижение просто. а само свойство constructor -- как было скрытым, так и осталось.

nerv_ 24.11.2014 19:41

Цитата:

Сообщение от krutoy
Это лишнее телодвижение просто

вот почему у тебя карма в минусе :D

devote 24.11.2014 19:58

Цитата:

Сообщение от krutoy
Вы явно переопределили ссылку, скрытую, которая там и так была.

да была, но другая... если Вы код читать умеете, должны понимать какая именно там была и какая стала и почему и для чего.

krutoy 24.11.2014 20:15

devote,
Да, я ошибся, невнимательно глянул, извиняюсь. Но код ваш работает и без этого. Зачем Вы это сделали?

krutoy 24.11.2014 20:18

Цитата:

Сообщение от devote
почему и для чего

Вот это, как раз интересует:)

tsigel 24.11.2014 20:35

При наследовании у ребенка в конструкторе получается ссылка на конструктор родителя. необходимо восстановить ребенку в конструкторе ссылку на свой конструктор.

krutoy 24.11.2014 21:18

tsigel,
Но тогда экземпляры FormFiled, будут ссылаться на FormFile
function FormField(data) {
  this._d = data;
}
  
function FormFile(data, opts) {
  FormField.call(this, data);
  this.opts = opts;
}
  
FormFile.prototype = FormField.prototype;
FormFile.prototype.constructor = FormFile;
  
o=new FormField()
console.log(o.constructor)
// [Function: FormFile]

devote 24.11.2014 21:22

Цитата:

Сообщение от krutoy
Но тогда экземпляры FormFiled, будут ссылаться на FormFile

тут моя ошибка, нужно примерно так:
function FormField(data) {
  this._d = data;
}
   
function FormFile(data, opts) {
  FormField.call(this, data);
  this.opts = opts;
}
   
FormFile.prototype = Object.create(FormField.prototype);
FormFile.prototype.constructor = FormFile;
   
o=new FormField()
console.log(o.constructor); // [Function: FormField]

o=new FormFile()
console.log(o.constructor); // [Function: FormFile]

krutoy 24.11.2014 22:25

devote,
Печально все таки, что эти гребаные комитетчики ломают язык. Если бы __proto__ не было deprecated, можно было бы обойтись без бубнов
function FormField(data) {
  this._d = data;
}
    
function FormFile(data, opts) {
  FormField.call(this, data);
  this.opts = opts;
}
    
FormFile.prototype.__proto__=FormField.prototype
    
o=new FormField()
console.log(o.constructor); // [Function: FormField]
 
o=new FormFile()
console.log(o.constructor); // [Function: FormFile]

//  [Function: FormField]
//  [Function: FormFile]

Одна надежда -- на setPrototypeOf. Я надеюсь, она будет обладать функциональными возможностями __proto__?

nerv_ 24.11.2014 22:49

krutoy, на здоровье :)
/**
 * Inherits a target (Class_1) from a source (Class_2)
 * @param {Function} target
 * @param {Function} source
 */
function inherit(target, source) {
    target.prototype = Object.create(source.prototype);
    target.prototype.constructor = target;
    target.super_ = source;
}

// ---------------------

function FormField(data) {
    this._d = data;
}

// -------------------------

inherit(FormFile, FormField);

function FormFile(data, opts) {
    FormFile.super_.apply(this, arguments);
    this.opts = opts;
}

// -------------------------

var a = new FormFile(1, 2);
var b = new FormFile(10, 22);
console.log(a, a instanceof FormField, b instanceof FormField);
console.log(b, a instanceof FormFile, b instanceof FormFile);

krutoy 24.11.2014 23:06

nerv_,
Это можно и проще сделать
Object.prototype.extend=function(src){
   for(var i in src){if(i in this) continue; this[i]=src[i]}
}

function FormField(data) {
  this._d = data;
}
    
function FormFile(data, opts) {
  FormField.call(this, data);
  this.opts = opts;
}
    
FormFile.prototype.extend(FormField.prototype)
    
o=new FormField()
console.log(o.constructor); // [Function: FormField]
 
o=new FormFile()
console.log(o.constructor); // [Function: FormFile]

Но, тем не менее...

devote 24.11.2014 23:15

Цитата:

Сообщение от krutoy
Это можно и проще сделать

это уже не наследование а копирование свойств.

krutoy 24.11.2014 23:25

devote,
Да, ступил, извиняюсь.

krutoy 24.11.2014 23:44

devote,
Вот так, кажись, можно
Object.prototype.inherit=function(parent){
    var o = Object.create(parent.prototype)
    o.constructor=this.prototype.constructor
    this.prototype=o
}

function FormField(data) {
  this._d = data;
}
    
function FormFile(data, opts) {
  FormField.call(this, data);
  this.opts = opts;
}
    
FormFile.inherit(FormField)
    
o=new FormField()
console.log(o.constructor); // [Function: FormField]
 
o=new FormFile()
console.log(o.constructor); // [Function: FormFile]

devote 25.11.2014 00:12

krutoy,
да, все это сахар конечно

kostyanet 25.11.2014 07:19

devote, получается вроде наоборот. После

FormField.call(this, data);

вся тряхомудия FormFile будет перезаписана, или я все еще туплю. Общий чертеж как сейчас работает.

function FormField(elem){
 var // из елемента достаются всякие атрибуты единовременно
  ;

 return {
   elem:elem,
   validate:function(event){if(type in rules)//...},
   update:function(value){elem.value=value},
 }

}

function FormFile(elem){
 var // из елемента достаются всякие атрибуты единовременно
  ;

 return {
   elem:image_list,
   validate:function(){// тут все голимо, ибо такое поле обычно одно, ну или 2, х с ним},
   update:function(html){image_list.innerHTML=html;},
 }

}

// по change и input формы вызывается внешнее управление
var validate = function(event){
  var elem=event.target;
  try{
    if(elem.name && elem.name in inputs) // inputs - это коллекция тех самых
       inputs[elem.name].validate(event);
  }
  catch(err){
   // еррор хендлер, в частности
   inputs[name].elem.classList.add('invalid');
  }
};

// по onload xmlhttprequest еще раз внешнее управление
for(var name in data)
   inputs[name].update(data[name]);


Ну вот, так сейчас я сделал, все блин работает, почему - не понятно. То есть понятно - за счет особенности js грести под методы и кложи контекст. В терминах php хрень непостижимая. Вероятно и не надо тут пытаться воспроизвести классическое наследование. Те же задачи решаются просто в другой плоскости.

То есть у меня 2 как бы объекта с одинаковым интерфейсом. При чем про свой этот интерфейс объекты не знают, ибо он снаружи, а внутри чисто приватные переменные, которые загружаются из контекста. Чтобы до них добраться снаружи нужен getter, чтобы им присваивать нужен setter. Я почитал что вещь довольно новая и как-то не рискнул.

kostyanet 25.11.2014 07:41

НИпотеме. А вот в php сеттеры и геттеры это капец какая халява. Это же еба...шся проверять isset'ом туевую хучу пропертей в рендере. Ну вот, делается 1 геттер типа

protected function __get($name){
  if(array_key_exists($name,$this->_d))
    return $this->_d[$name];
  else
    return null;
}


и потом значит повсюду if($this->value), или там if($name=$this->name).

Как обычно в бочке - ложка: нормально нельзя получить референс через геттер и нельзя присвоить значение элементу массива в такой вот недоступной переменной, которая array. Если же все это николебет - то голимая лафа.

А вот писать везде if(typeof some_var === 'undefined') меня уже подзаебало. JS же в упор не хочет отвечать за вложение больше 1 глубиной. На php if(isset(arr['one']['two']['three']['four']))... на js - х нанэ. Или как-то делается?

devote 25.11.2014 10:02

Цитата:

Сообщение от kostyanet
А вот писать везде if(typeof some_var === 'undefined') меня уже подзаебало. JS же в упор не хочет отвечать за вложение больше 1 глубиной. На php if(isset(arr['one']['two']['three']['four']))... на js - х нанэ. Или как-то делается?

кто то мешает написать подобие isset?

простейший вариант:
function isset(object, keys) {
  keys = (keys instanceof Array ? keys : [keys]).slice(0);
  return object !== void 0 && keys[0] in object
    ? keys.length === 1 || isset(object[keys[0]], keys.slice(1)) : false;
}

var o = {
  a: {
    w: {
      r: {
        t: {
          y: 1
        }
      }
    }
  }
};

alert(isset(o, 'a')); // true
alert(isset(o, 'w')); // false
alert(isset(o, ['a', 'w', 'r', 't', 'y'])); // true

Erolast 25.11.2014 11:25

Цитата:

а где он таков есть?
Тьфу, да, нигде, попутал.

Цитата:

Печально все таки, что эти гребаные комитетчики ломают язык. Если бы __proto__ не было deprecated, можно было бы обойтись без бубнов
Ну, во-первых, на замен приходит полностью аналогичный функционально Object.setPrototypeOf, во-вторых, в ES6, где __proto__ устарел, этих бубнов и не понадобится - в нем есть нормальные классы.

devote 25.11.2014 11:51

Цитата:

Сообщение от Erolast
этих бубнов и не понадобится - в нем есть нормальные классы.

а для старых браузеров есть __proto__ :) Хоть он и deprecated но мелкософт в свой ИЕ11 все же добавил это свойство)

kostyanet 25.11.2014 12:58

Цитата:

Сообщение от devote
кто то мешает написать подобие isset?

Не так должно быть, а так как есть

if(isset(obj.some.prop.on.the.deep))

не важно, такие вещи полифилами не сделаешь, или сделаешь угребище. Я прежде чем пожаловаться вам уже пожаловался самому себе и поискал как люди делают - да нихера не делают. Максимум забубенят глобально U='undefined' и вперде.

kostyanet 25.11.2014 13:24

Все равно извилинами не понимаю как оно работает, только интуицией, по которой все и сделал. Вот код с купюрами лишнего:

var FormField=function(elem){

	var	type=		elem.type,
		title=		elem.title,
		pattern=	elem.getAttribute('pattern'),
		required=	elem.getAttribute('required'),
		multiple=	elem.getAttribute('multiple'),
		errmsg=		elem.getAttribute('errmsg')+elem.getAttribute('placeholder'),
		value=		null,
		rules={ // тут несколько функций проверки длины, паттерна и тп};

	return {
		elem:elem,
		update:function(v){
			elem.value=v;
		},
		validate:function(event) {
			value=elem.value.trim();
			if(type in rules)
				rules[type](event);
			else
				rules.text(event);
		}
	};
};


В секции загрузки цикл типа

// сначала проверки всякие, типа кто без имени, кто без типа, 
// чекбоксы с радивами - пропустить, что вернется - то запишется в коллекцию

	if(type=='file')
		return FormFile(elem,progress);
	else
		return FormField(elem);


Я правильно понимаю что функция выполняется и возвращается результат - объект.

Но с чем он связан - с текстом функции как это я предполагаю реализуется во всяких onclick/onload, или где-то хранится уже интерпретированный экземпляр функции откуда и черпаются данные в этот объект. Блин, даже толком задать вопрос не могу.

Короче, тут функция копируется вместе с тем что возвращается, или ее логика существует в единственном экземпляре?

kostyanet 25.11.2014 13:29

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

Кстати, может оно все работает только потому что elem это live html element?

kostyanet 26.11.2014 06:51

Не оставляю попыток понять где хобот, а где хвост. Вопрос такой, что делает код

function object(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }


кроме ничего?

из статьи http://javascript.crockford.com/prototypal.html

Ну то есть я сделал по чертежу - и получил то же самое.

var obj = BaseClass(opts);
var inh=object(BaseClass(opts));

совершенно эквипенисуально. Наследуется от ничего, от F(). Меня вся эта хрень капец как запутала.

kostyanet 26.11.2014 07:04

Я предложил целлулоидную метафору действующего вещества канонического наследования. Примитивно говоря, техническим языком без метафор - это merge. С учетом scope и все такое, что меня не касается, поскольку делается на уровне интерпретатора.

Кто-нибудь может предложить метафору квази-наследования в JS, действующее вещество так сказать? Что за хрень этот прототип - прототипом не являющийся?

Если это типа lazy binding

$obj=new Object();
$obj->prototype=new ProtoObject();

и затем на уровне интерпретатора ослеживание по ключевому слову всех такого рода связей, то никакого наследования тут вообще нет. В объект захерачивается столько слоев, сколько их профтыкано в прототип. Как при этом перезаписать/перекрыть свойство или метод? Ну то есть как сделать интерфейс и затем его наполнить содержанием?

krutoy 26.11.2014 09:46

Цитата:

Сообщение от kostyanet
Вопрос такой, что делает код

При вызове функции создается пустая функция, на нее вешается в качествe prototype o, и она возвращает экземпляр F

jsnb 26.11.2014 10:13

Цитата:

Сообщение от kostyanet (Сообщение 342930)
Вопрос такой, что делает код
function object(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

Создает новый объект, у которого __proto__ ссылается на o.
var someObj = {someProp: 'somevalue'};
var inhObj = object(someObj);

//аналогично этому:
var someObj = {someProp: 'somevalue'};
var inhObj = {};
inhObj.__proto__ = someObj;


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