Резюме
Я пытался добиться наследования и инкапсуляции должным образом в javascript, как это было в языке на основе классов, таких как c #.
Уродливая часть заключается в том, что защищенные члены имеют несколько копий в частных экземплярах, которые доступны только через замыкание, и у меня нет идеи, кроме как обновить эти элементы для частных экземпляров.
Если это возможно, я хочу избавиться от
transmit
иtransfer
в моем кодеFunction.extend
.Обновление Для людей, которые заинтересованы в цитировании или исследованиях, здесь хранилище исходного кода:
История
Поскольку сборки могут быть концепцией, которая выходит за рамки javascript, я не принимаю во внимание модификатор
internal
, ноpublic
,protected
иprivate
.Модификаторы
public
иprivate
не так сложны; но с наследованием,protected
значительно сложнее. Тем не менее, это не рекомендуется делать с javascript, в большинстве статей, которые я прочитал, говорится о префиксе со специальным символом и документировании.Но, похоже, я настаиваю на том, чтобы сделать javascript для имитации языков, основанных на классах. Я украл эту идею и реализовал по-своему, код находится в конце этого поста.
Идея, стоящая за сценой, состоит в том, чтобы обеспечить более высокую доступность с более высоким прототипом и получить доступ к самому высокому с помощью замыкания.
Скажем, у нас есть три прототипа
A
,D
иG
, это выглядит какПоскольку невозможно, чтобы объект был экземпляром типа также другого типа, которого нет в цепочке прототипов; способ, который я выбрал, состоит в том, чтобы связать уровень
protected
по горизонтали и скопировать элементы из прототипа декларирующего типа. Это делает возможным класс вложенности, потому что члены, объявленные с менее производным типом, могут быть распространены на более производные типы; методtransmit
в моем коде должен сделать это. ЕслиA
,D
иG
имеют своих защищенных членов, это будет выглядеть так:Закрытие для доступа к частному экземпляру -
this['']
. Он принимает аргумент для идентификации класса. Держатель модификаторов - это просто идентификатор класса, названныйy
вFunction.extend
и_
в тестовом коде, его нельзя показывать вне объявления класса. Он также используется в качестве ярлыкаthis['']
._['base']
фактически является не только инициатором базового конструктора, но и создателем частных экземпляров. Он создает частные экземпляры и обновляетthis['']
для каждого конструктора с наследованием, поэтому его всегда следует вызывать в конструкторах.Хотя частный экземпляр будет иметь доступ к открытым членам, его не следует использовать для их изменения, поскольку не гарантируется, что
this['']
будет вызываться при доступе к открытым членам. Но доступ к частному экземпляру есть;recent
запоминает последний доступный частный экземпляр и обновляет защищенные элементы, если есть изменения.У меня вопрос, как я могу избавиться от такого рода обновления защищенных членов? Есть ли лучшие идеи для более реалистичной инкапсуляции?
ps.: Я на самом деле не хочу решения, которое использует нестандартные методы/свойства... и было бы лучше, если есть полифиллы, если используемые методы/свойства слишком модны для старых браузеров.
Function.extend
Function.extend=function(base, factory) { factory.call(initializeClass); updateStaticMembersOfDerivedInnerClasses(y['public'].constructor); transfer(y['protected'], y['public']); return y['public'].constructor; function y($this) { return $this[''](y); } function transfer(target, source, descriptor) { if(target!==source? 'undefined'!==typeof target? 'undefined'!==typeof source: false:false) { var keys='undefined'!==typeof descriptor? descriptor:source; for(var key in keys) { if(Object.prototype.hasOwnProperty.call(source, key)) { target[key]=source[key]; } } } } function updateStaticMembersOfDerivedInnerClasses(outer) { var member, inner; for(var key in outer) { if(Object.prototype.hasOwnProperty.call(outer, key)? (member=outer[key]) instanceof outer? outer!==(inner=member.constructor): false:false) { transfer(inner, outer); } } } function initializeInstance() { var $this=Object.create(y['private']); var derivedGet=this['']; var recent=$this; this['']=function(x) { var value=y!==x? derivedGet.call(this, x):$this; if(value!==recent) { transfer(value, recent, x['protected']); recent=value; } transfer(value, this); return value; }; base.apply(this, arguments); $this['']=this['']; } function initializeClass(derived) { y['public']=Object.create(base.prototype); y['public'].constructor=derived; if(Object.prototype.hasOwnProperty.call(base, 'transmit')) { base.transmit(y); } else { y['protected']=Object.create(y['public']); } y['private']=Object.create(y['protected']); y['base']=initializeInstance; transfer(derived, base); derived.transmit=function(x) { if(x['public'] instanceof derived) { x['protected']=Object.create(y['protected']); x['protected'].constructor=x['public'].constructor; } }; derived.prototype=y['public']; return y; } };
тестовый код
'use strict'; var BaseClass=Function.extend(Object, function () { var _=this(BaseClass); var NestedClass=Function.extend(BaseClass, function () { var _=this(NestedClass); function NestedClass(x, y, z) { _['base'].apply(this, arguments); _(this).Y=y; _(this).Z=z; } _['public'].SetX=function (x) { _(this).InternalSetX(x); }; _['public'].GetX=function () { return _(this).InternalGetX(); }; _['public'].GetY=function () { return _(this).Y; }; _['public'].SetZ=function (z) { _(this).Z=z; }; _['public'].GetZ=function () { return _(this).Z; }; _['private'].Y=0; }); function BaseClass(x) { _['base'].apply(this, arguments); _(this).X=x; } _['protected'].InternalSetX=function (x) { _(this).X=x; }; _['protected'].InternalGetX=function () { return _(this).X; }; _['private'].X=0; _['protected'].Z=0; BaseClass.Sample=new NestedClass(1, 2, 3); }); var DerivedClass=Function.extend(BaseClass, function () { var _=this(DerivedClass); function DerivedClass(x, y, z) { _['base'].apply(this, arguments); } }); var o=DerivedClass.Sample; alert(o.GetX()); alert(o.GetY()); alert(o.GetZ()); o.SetX(3); o.SetZ(1); alert(o.GetX()); alert(o.GetY()); alert(o.GetZ());