-
Summary
我尝试在 javascript 中正确实现继承和封装,就像在基于类的语言(例如 c#)中一样。
丑陋的部分是受保护的成员在私有实例中有多个副本,这些副本只能通过闭包访问,除了将这些成员刷新到私有实例之外我没有任何想法。
如果可以的话我想把两者都摆脱掉transmit
and transfer
在我的代码中Function.extend
.
-
Update对于有兴趣引用或研究的人,这里是源代码存储库:
https://github.com/kenkins/Function.extend https://github.com/kenkins/Function.extend
-
故事
Since 组件可能是一个超出了javascript范围的概念,我不接受internal
考虑到修改器,但是public
, protected
and private
.
public
and private
修饰符并不难实现;但有了继承,protected
非常棘手。然而,我读过的大多数文章都说,这不是推荐使用 javascript 做的事情带有特殊字符的前缀并记录它.
但看来我坚持让javascript来模拟基于类的语言..我偷了这个想法 https://stackoverflow.com/questions/20149166/prototype-closure-in-the-constructor/20151610#20151610并以我的方式实现,代码位于本文的后面。
幕后的想法是通过更高的原型来提供更高的可访问性,并通过闭包访问最高的原型。
假设我们有三个原型A
, D
and G
, 看起来像
因为一个对象不可能既是一种类型的实例,又是不在原型链中的另一种类型的实例;我选择的方式是链接protected
水平放置并从声明类型的原型中复制成员。这使得嵌套类成为可能,因为在派生程度较低的类型上声明的成员可以传播到派生程度较高的类型;这transmit
我的代码中的方法就是这样做的。如果A
, D
and G
有自己的受保护成员,它看起来像:
访问私有实例的闭包是this['']
。它需要一个用于识别类的参数。修饰符持有者只是类标识符,名为y
in Function.extend
and _
在测试代码中,它不应该暴露在类声明之外。它也被用作快捷方式this['']
.
_['base']
实际上不仅是基本构造函数调用者,而且还是私有实例创建者。它创建私有实例并更新this['']
对于具有继承的每个构造函数,因此应始终在构造函数中调用它。
尽管私有实例可以访问公共成员,但不应该使用它来更改它们,因为this['']
不保证在访问公共成员时被调用。但私有实例的访问是;recent
记住最近访问的私有实例,并在发生更改时更新受保护的成员。
我的问题是,如何摆脱这种刷新受保护成员的情况?有没有更好的想法来实现更真实的封装?
p.s.:我实际上不想要一个使用非标准方法/属性的解决方案..如果使用的方法/属性对于旧浏览器来说太时尚的话,最好有polyfills。
-
函数.扩展
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());
我也有类似的想法,决定尝试写点东西。一个普通的 js 解决方案。虽然还早,但我喜欢它的结果。您可能也会觉得很有趣。
它不完全是 C#,但提供了更严格的生态系统。以及轻量级解决方案中的其他一些高级 js 功能。
https://github.com/iamlothian/rucksack.js https://github.com/iamlothian/rucksack.js
这不是您的代码的解决方案,而是您的概念的解决方案。如果您的目标是让您的想法发挥作用,那么请务必继续,因为我对结果很感兴趣。
如果你像我一样只是想要一个更加结构化的 js 环境,那么这是我写的一个与你的问题概念类似的野心。
Part 2:
这里的想法是使用闭包和访问限制来创建一个模式,限制定义后代码的使用和更改方式。大部分艰苦的工作已经完成。但该模式留给您来定义。
这是一个快速模拟示例,演示如何实现 public|protect|private 继承。我正在尝试决定我是否将其中一些实现为内置功能,或者让用户实现他们自己的对象扩展,就像我在示例中所做的那样。
http://plnkr.co/edit/ao2hTyBV1b3nYIwr7ZS5 http://plnkr.co/edit/ao2hTyBV1b3nYIwr7ZS5
实现在scripts.js 中。查看控制台以了解发生了什么情况。
Rucksack 提供的是一个用于创建独立的代码模块的框架。这些模块被分组到命名空间中并且可以相互依赖。这些依赖项按照定义延迟解析,因此定义顺序并不重要。解析过程提供了一些其他有用的功能,例如接口匹配和密封模块。
目前的特点:
- Modular
- 依赖注入
- 工厂构造函数(实例对象)
- 服务构造函数(静态对象)
- 延迟加载
- 简单的错误记录(模块内的所有错误都会被捕获并可以传递)
- 命名空间
- 可密封模块和命名空间(无法从命名空间外部访问的模块)
- 模块的全局等待事件
- 可选配置对象的接口
- 可选的严格接口注入检查
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)