如何在JavaScript中实现C#访问修饰符?
-
概要
我试图在JavaScript中正确地实现inheritance和封装,就像使用基于类的语言(如c#)一样。
丑陋的部分是受保护的成员在私人实例中有多个副本,只能通过闭包进行访问,除了将这些成员刷新到私有实例之外,我没有其他想法。
如果可能的话,我想摆脱
Function.extend
代码中的transmit
和transfer
。 -
故事
由于程序集可能是一个超出javascript范围的概念,我不考虑
internal
修饰符,但public
,protected
和private
。public
和private
修改者并不难实现; 但是对于inheritance,protected
是非常棘手的。 然而,这不是一个推荐的事情,我读过的大多数文章说, 前缀与一个特殊的字符和文件 。但似乎我坚持让JavaScript来模拟基于类的语言..我偷了这个想法,并以我的方式实现,代码是在这篇文章的后面。
现场背后的想法是把更高的可达性与更高的原型,并访问最高的一个封闭。
假设我们有三个原型
A
,D
和G
,看起来像因为一个对象不可能是一个不属于原型链的另一种types的实例。 我select的方式是水平链接
protected
级别,并从声明types的原型复制成员。 这使嵌套类成为可能,因为在派生较less的types上声明的成员可以传播到更多派生types; 在我的代码中的transmit
方法是做到这一点。 如果A
,D
和G
拥有自己的受保护成员,则看起来像:访问私有实例的闭包是
this['']
。 它需要一个用于识别类的参数。 修饰符持有者只是类标识符,在Function.extend
名为y
,在testing代码中是_
,它不应暴露在类声明之外。 它也被用作this['']
的快捷键。_['base']
实际上不仅是基础构造函数调用者,而且是私有实例创build者。 它创build私有实例并为每个构造函数更新this['']
的inheritance,所以它应该总是在构造函数中调用。尽pipe一个私有实例可以访问公共成员,但是不应该被用来改变它们,因为
this['']
在访问公共成员时并不保证被调用。 但是私人实例的访问是;recent
记得最近访问的私有实例,如果有变化,更新受保护的成员。我的问题是,我怎样才能摆脱这种清爽的保护成员? 有更好的想法来实现封装更多的现实?
PS:我实际上不想要一个使用非标准方法/属性的解决scheme。如果使用的方法/属性对于旧的浏览器来说太过时尚,它会更好。
-
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 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; } 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']); } derived.transmit=function (x) { if (x['public'] instanceof derived) { x['protected']=Object.create(y['protected']); x['protected'].constructor=x['public'].constructor; } }; y['private']=Object.create(y['protected']); y['base']=initializeInstance; transfer(derived, base); derived.prototype=y['public']; return 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]; } } } }
-
testing代码
'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解决scheme。 还早,但我喜欢它出来的。 你也许会觉得它也很有趣。
这不完全是C#,但提供了一个更严格的生态系统。 和其他一些先进的jsfunction在一个轻量级的解决scheme。
这不是解决你的代码,而是解决你的概念。 如果你的目标是让你的想法工作,然后通过一切手段继续下去,因为我感兴趣的结果。
如果你喜欢我只是想要一个更结构化的JS环境,那么这里是我写的与你的问题概念类似的野心。
第2部分:
这里的想法是使用闭包和访问限制来创build一个模式,限制代码在被定义之后被使用和改变的方式。 大部分的努力工作已经完成了。 但模式是留给你定义的。
下面是一个快速的模拟示例,演示如何实现public | protected | privateinheritance。 我正在试图决定天气我实现了一些作为内置function,或者留给用户来实现自己的对象扩展,就像我在例子中。
http://plnkr.co/edit/ao2hTyBV1b3nYIwr7ZS5
实现在scripts.js中。 查看你的控制台,看看发生了什么事情。
什么背包提供了一个框架来创build分离的代码模块。 这些模块被分组到命名空间中,并可以相互依赖。 这些依赖关系是按照定义的懒惰解决的,所以定义顺序并不重要。 parsing过程提供了一些其他有用的function,如接口匹配和密封模块。
目前的function:
- 模块化
- dependency injection
- 工厂构造函数(Instances Object)
- 服务构造函数(静态对象)
- 延迟加载
- 简单的错误logging(模块中的所有错误都被捕获并可以传递)
- 命名空间
- Sealable模块和命名空间(无法从命名空间外部访问的模块)
- 全局等待事件模块
- 可选configuration对象的接口
- 可选的严格的接口检查注射
虽然封闭的代码可能会解决你想要的,但我会用更简单的特权方法,就像Crockford在这里调用它们一样 。
用法思路很简单:
- 在基础对象上定义特权方法(限制1 – 只允许调用一次)。
- 特权方法返回一个包含其中受保护函数的自身(基础对象)的受保护接口(可能这些函数是在基础中私有定义的,然后被复制到受保护接口对象中…或者受保护接口可能存在私下)。
- 每个对象都使用其基础对象的受保护接口来扩展受保护的接口,并通过特权方法公开它。
你最终会得到这样的结果:
function A() { var protected = { protectedA: function() { } }; this.getProtected = (function() { var allow = true; //privileged function. return function() { if (allow) { allow = false; return protected; } }; }); } //B - derives from (extends) A function B() { var base = {}; //acquiring a base omitted - depends on your implementation. var protected = { protectedB: function() { } }; //"extend" simply copies new members into protected: protected = $.extend(protected, base.getProtected()); this.getProtected = function() { /* privileged function - similar to A.getProtected */ }; }
JavaScript在这个范围内的能力有限,所以protected
糖无论如何都会带来一些成本。
Javascript是一门广泛的语言,因为你可以在网页上做几乎所有你想做的事情,只要创build一些function并find方法就可以了。
我可以告诉你,JavaScript不是一种安全的语言,因为你可以很容易地访问大部分的variables和函数,阅读它们,并知道它是如何工作的,只需要访问页面上的.js文件即可。
My Tought:由于开发人员已经知道可能没用,因为JavaScript不会“旅行”到另一个地方(页面),除非使用会话variables,否则一些访问修饰符并未在javascript中创build。
而关于这些修饰符:
-
私人的
-
受保护
- 上市
我可以告诉你,我知道一些JavaScript修饰符有一些相似之处,那就是:
本地:
var Variable = 0;
自动地,这被转换成一个Integervariables,因为它正在接收一个Integer值,而且这也是一个LOCALvariables,因为var
修饰符声明这个variables的方式是你不能访问它的值,除非你在这个variables被声明的同一个函数里面。
例:
如果你用这种方式声明这些函数,使用默认的修饰符:
function conflict(){ i = 2; changeI(); alert(i); } function changeI(){ i = 0; }
在这种情况下, i
是两个函数的variables。
所以如果你执行conflict();
你会得到一个结果为0
的警报。
但是,如果你声明i
使用var
修饰符:
function conflict(){ var i = 2; changeI(); alert(i); } function changeI(){ var i = 0; }
在这种情况下,你有两个i
variables,因为它们只能在函数内部使用,所以如果你执行了conflict();
现在,你会得到值为2
的警报。
类variables:
this.Variable = "a";
这个variables自动是一个string,因为它正在接收一个string值,可能你已经知道this
修饰符做了什么,但是,我会试着用我的观点来解释,那就是这个variables是来自于SuperClass还是in javascript可以称为Class的“SuperFunction”,换句话说,就是“父亲”类。
一个例子:
function TClass() { this.getVar = function() { try { return "test"; } catch(err) { return false; } } this.alertVar = function() { try { alert(this.getVar()); } catch(err) { alert('error'); } } } var $Class = new TClass();
正如你在上面看到的,我创build了一个类TClass
和一些包含函数的variables(javascriptclosures),并添加了修饰符this.
给他们,使他们绑定到TClass
,就像你在alertVar()
函数中看到的,我在alert(this.getVar());
alertVar()
访问alert(this.getVar());
在这种情况下来自TClass
的函数等于此。
而这部分: var $Class = new TClass();
我正在创build类,因为你可能知道,要访问它的方法,这样做我可以执行,testing:
$Class.alertVar();
结果,包含“testing”的警报箱,你可以看到:
请注意,您不能以其他方式访问TClass
方法,您只能访问创build类并通过它访问。
所以我希望你明白了this
修饰符的可用性。
全球:
window.Variable = true;
自动javascript声明这个variables是一个布尔值,因为它正在接收一个布尔值。 window
修饰符就像它说的那样,你可以访问它,不pipe你在窗口上是什么,因为javascriptvariables声明的时候,他们把这个DOM放到窗口里,看看什么是DOM:
DOM(文档对象模型): DOM是一个多平台,它表示HTML,xhtml和xml标记是如何由您正在使用的浏览器组织和读取的。 换句话说,如果你访问DOM,你可以看到当前浏览器上存在的每一个操作,每个variables或者这样的事物。
与其他variables不同的是, window
variables可以指定另一个值,并且可以访问实际的值,无论你是在一个函数内还是不在一个js文件中。
全局(窗口)示例:
在页面的onLoad
事件上执行声明window
variables的代码,或使用浏览器控制台自行声明:
window.Variable = true;
然后,添加一个包含这个函数的JS文件,或者通过在浏览器控制台上执行代码来自己创build它:
function testGlobalVar(){ if (Variable) alert("it works!"); }
当你执行testGlobalVar()
你会得到警报,但这只是因为你把它声明为`window',否则你什么也得不到。
默认修饰符:
Variable = 0.5
自动将此variables声明为Float,因为它正在接收Float值。 我不知道,如果你已经知道,但JavaScriptvariables声明为通常的方式,有一个默认的修饰符,使variables类似于window
variables,但你不能访问它从任何你是,但在大多数情况下,你可以访问它,特别,我不知道所有的情况下,你不能访问它,但我知道你不能当你加载一个JS文件,它是在里面声明。 只有当你运行一个函数声明它,然后尝试访问。
顺便说一下,我看到你想知道符合你所说的三个修饰符,但在我修改了一些修饰符,我告诉你可以用来做你的C#修饰符。
我希望你明白我在说什么。
啊,如果你在一个variables里面看到一个函数的时候感到困惑,那么研究一下Javascript闭包,你会明白的:)。
父母和孩子之间如何互动
-
一个扩展的子类调用
super.call
,一个函数,构造它的父母的一个实例。 -
父类通过在构造函数中使用
this.share
来共享它的受保护成员(包括字段和函数)。 -
一个子类也可以调用
super.fetch()
,它返回父类传给它的字段/函数的对象。
为了说明我的技术,下面的代码演示了OOP的一些基本要素,一个简单的例子就是class Dog extends Animal
这个面向对象模型的一些核心function
// runs in both node.js and browser var global_namespace = ('undefined'==typeof module)? window: global; // put a no-operation function in the value for `share` in case nothing is extending a class var not_extendable = {share:function(){}}; // when something is extending a class... var extendable = function(constructor) { // create a space for protected fields var protected_space = {}; // the following is what will get passed as `this` to the parent constructor var sharing = { share: function(fields) { protected_space = fields; }, }; // the following is what will get passed as the first arg to the child constructor return { // enables child to call its parent's constructor call: function(args) { return constructor.apply(sharing, args); }, // allows child to access protected fields shared by its parent fetch: function() { return protected_space; }, }; };
Animal
类
// class definition for `Animal` (function(namespace) { // construct an instance of this class var constructor = function(name, weight, info) { // private fields var color = (info && info.color) || 'unknown'; // for protected fields var protect = { weight: weight, noise: function() { return 'nothing'; }, }; // share the protected fields with any subclass that might be extending this this.share(protect); // public fields and methods return { speak: function() { console.log(name+' says '+protect.noise()); }, feed: function() { console.log(name+' is not hungry'); }, weigh: function() { console.log(name+' weighs '+protect.weight+' lbs'); }, toString: function() { return '{Animal}'; }, }; }; // handle calls to: `Animal()` namespace.Animal = function() { // called with new operator: `new Animal(...)` if(this !== namespace) { // construct simple instance of this class return constructor.apply(not_extendable, arguments); } // static call: `Animal(...)`, means the caller wants to extend this class else { // reference child constructor var child_constructor = arguments[0]; // return a wrapped constructor function return function() { // call child constructor and allow it to call the super constructor return child_constructor.apply({}, [extendable(constructor), arguments]); }; } }; })(global_namespace);
Dog
类
// class definition for `Dog` (function(namespace) { // class `Dog` extends class `Animal` var constructor = Animal(function(super_class, args) { // private field var been_fed = false; // call super's constructor var operator = super_class.call(args); // inherit parent's protected members var parent = super_class.fetch(); // override a protected method parent.noise = function() { return 'bark!'; }; // override a public method operator.feed = function() { been_fed = true; parent.weight += 5; }; // extend a public method var super_weigh = operator.weigh; operator.weigh = function() { super_weigh(); if(been_fed) console.log('\t'+args[0]+' has been eating :)'); else console.log('\t'+args[0]+' has not been fed yet'); }; // override another public method operator.toString = function() { return '{Dog}'; }, // return the operator (interfacable instance object) return operator; }); // handle calls to: `Dog()` namespace.Dog = function() { // called with new operator: `new Dog()` if(this !== namespace) { return constructor.apply(this, arguments); } // static call: `Dog()` else { // we do no allow extending class `Dog` return false; } }; })(global_namespace);
所以现在我们可以做到这一点:
var giraffe = new Animal('Mr. Giraffe', 720); giraffe.speak(); // "Mr. Giraffe says nothing" giraffe.weigh(); // "Mr. Giraffe weighs 720 lbs" var buddy = new Dog('Buddy', 50); buddy.speak(); // "Buddy says bark!" buddy.weigh(); // "Buddy weighs 50 lbs" // "Buddy has not been fed yet" buddy.feed(); buddy.weigh(); // "Buddy weighs 55 lbs" // "Buddy has been eating :)"
这允许私人,保护和公共领域/function。 保护和公共领域/function都可能被覆盖和扩展。
console.log(giraffe); // "{Animal}" console.log(buddy); // "{Dog}"
我一直在做另一个有趣的JavaScript项目,并实施了一些可能更接近你正在寻找的东西。
Implement.js
对你的想法感兴趣。