Еще один тупой вопрос по наследованию
В 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); и там профтыкаю все нормально, то, дальше не пойму что делать. У этих двух объектов кроме перечисленных пропертей - ничего общего. |
Если сначала сделать безымянный объект
var obj={ elem: elem, error: error, //... } то как его расширить методами которые как объект-же возвращают перечисленные "классы", то есть это функции var FormFile = function(opts,progress){ // внутренности return { // наружности }; }; |
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 } |
Переписать проперти по-одной я знаю как. Но в php я ничего не переписываю. Выше нарисовано как это происходит - массив с данными передается родителю, а тот кладет их в свою приватную переменную и - добавляю теперь - выдает через геттер по требованию. Если без геттера, то он бы их просто выжал (extract) в свой this и естественно этот this стал бы this для наследника.
Метафора классического наследования в программировании - целлулоид. Например есть абстрактный класс с чертежом. На этот класс кладется целлулоид и по нему рисуются детали, фон, кое-что полностью перерисовывается. Затем на этот целлулоид кладется следующий целлулоид, который заменяет или модифицирует часть, добавляет еще деталей, цвета, формы итд. Пачка целлулоидов формирует картину. В ран-тайм - анимацию. Так вот жаба-наследование не классическое и метафора у него типа участок электрической цепи с индукцией на свои проперти. Я ее не могу осилить до сих пор. |
Не приватную - 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 |
В php главная задача рендерить контент, поэтому из строки массива делается объект, который сам себя рендерит и коллекция сама себя рендерит, в итоге там где хочется просто пишешь echo $this->form; и вся эта тряхомудия в мгновение ока превращается в голимую кучу хтмля.
В js такой задачи у меня нет, но именно долбаное поле file требует рендера и поэтому я подумал что надо просто сделать так же, как в php и будет работать. Оно работает, но через жопу. То есть приватные методы нихера не знают про все эти проперти, потому что они торчат наружу, а как их внутрь завести я еще не понял. |
Цитата:
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 ] Цитата:
Цитата:
По сабжу: PHP не знаю, поэтому код твой не понимаю. Описание задачи -- тоже мутное. Может быть, если бы ты описал задачу максимально абстрактно, я бы мог тебе помочь, если бы смог понять, что ты хочешь. |
Я не понимаю зачем вы тогда отвечаете если не понимаете.
|
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); |
Конструктор не должен быть enumerable.
|
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); точней, делает то, что перезаписывает скрытую ссылку на явную. |
Цитата:
Цитата:
где вы тут увидели открытие перечислимости конструктора? прогоните объекты a или b через for...in и посмотрите где там перечисляется конструктор? |
Цитата:
Хотя, по хорошему, на это пофиг. |
devote,
Я не так выразился. Вы явно переопределили ссылку, скрытую, которая там и так была. Это лишнее телодвижение просто. а само свойство constructor -- как было скрытым, так и осталось. |
Цитата:
|
Цитата:
|
devote,
Да, я ошибся, невнимательно глянул, извиняюсь. Но код ваш работает и без этого. Зачем Вы это сделали? |
Цитата:
|
При наследовании у ребенка в конструкторе получается ссылка на конструктор родителя. необходимо восстановить ребенку в конструкторе ссылку на свой конструктор.
|
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] |
Цитата:
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] |
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__? |
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); |
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,
Да, ступил, извиняюсь. |
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] |
krutoy,
да, все это сахар конечно |
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. Я почитал что вещь довольно новая и как-то не рискнул. |
НИпотеме. А вот в 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 - х нанэ. Или как-то делается? |
Цитата:
простейший вариант: 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 |
Цитата:
Цитата:
|
Цитата:
|
Цитата:
if(isset(obj.some.prop.on.the.deep)) не важно, такие вещи полифилами не сделаешь, или сделаешь угребище. Я прежде чем пожаловаться вам уже пожаловался самому себе и поискал как люди делают - да нихера не делают. Максимум забубенят глобально U='undefined' и вперде. |
Все равно извилинами не понимаю как оно работает, только интуицией, по которой все и сделал. Вот код с купюрами лишнего:
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, или где-то хранится уже интерпретированный экземпляр функции откуда и черпаются данные в этот объект. Блин, даже толком задать вопрос не могу. Короче, тут функция копируется вместе с тем что возвращается, или ее логика существует в единственном экземпляре? |
В этом "объекте" собственно ничего и нет, кроме простейшей валидации, а а файловый сука жирный. Если даже он целиком превращается в инстансь - то фиг с ним, таких полей обычно всего 1. Но блин я тут вообще не понимаю что станет экземпляром, а что останется в чертеже.
Кстати, может оно все работает только потому что elem это live html element? |
Не оставляю попыток понять где хобот, а где хвост. Вопрос такой, что делает код
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(). Меня вся эта хрень капец как запутала. |
Я предложил целлулоидную метафору действующего вещества канонического наследования. Примитивно говоря, техническим языком без метафор - это merge. С учетом scope и все такое, что меня не касается, поскольку делается на уровне интерпретатора.
Кто-нибудь может предложить метафору квази-наследования в JS, действующее вещество так сказать? Что за хрень этот прототип - прототипом не являющийся? Если это типа lazy binding $obj=new Object(); $obj->prototype=new ProtoObject(); и затем на уровне интерпретатора ослеживание по ключевому слову всех такого рода связей, то никакого наследования тут вообще нет. В объект захерачивается столько слоев, сколько их профтыкано в прототип. Как при этом перезаписать/перекрыть свойство или метод? Ну то есть как сделать интерфейс и затем его наполнить содержанием? |
Цитата:
|
Цитата:
var someObj = {someProp: 'somevalue'}; var inhObj = object(someObj); //аналогично этому: var someObj = {someProp: 'somevalue'}; var inhObj = {}; inhObj.__proto__ = someObj; |
Часовой пояс GMT +3, время: 06:37. |