如何在JavaScript中实现C#访问修饰符?

  • 概要

    我试图在JavaScript中正确地实现inheritance和封装,就像使用基于类的语言(如c#)一样。

    丑陋的部分是受保护的成员在私人实例中有多个副本,只能通过闭包进行访问,除了将这些成员刷新到私有实例之外,我没有其他想法。

    如果可能的话,我想摆脱Function.extend代码中的transmittransfer

  • 故事

    由于程序集可能是一个超出javascript范围的概念,我不考虑internal修饰符,但publicprotectedprivate

    publicprivate修改者并不难实现; 但是对于inheritance, protected是非常棘手的。 然而,这不是一个推荐的事情,我读过的大多数文章说, 前缀与一个特殊的字符和文件

    但似乎我坚持让JavaScript来模拟基于类的语言..我偷了这个想法,并以我的方式实现,代码是在这篇文章的后面。

    现场背后的想法是把更高的可达性与更高的原型,并访问最高的一个封闭。

    假设我们有三个原型ADG ,看起来像

    BANxt.png

    因为一个对象不可能是一个不属于原型链的另一种types的实例。 我select的方式是水平链接protected级别,并从声明types的原型复制成员。 这使嵌套类成为可能,因为在派生较less的types上声明的成员可以传播到更多派生types; 在我的代码中的transmit方法是做到这一点。 如果ADG拥有自己的受保护成员,则看起来像:

    bhcsI.png

    访问私有实例的闭包是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。

iamlothian/rucksack.html

这不是解决你的代码,而是解决你的概念。 如果你的目标是让你的想法工作,然后通过一切手段继续下去,因为我感兴趣的结果。

如果你喜欢我只是想要一个更结构化的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; } 

在这种情况下,你有两个ivariables,因为它们只能在函数内部使用,所以如果你执行了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不同的是, windowvariables可以指定另一个值,并且可以访问实际的值,无论你是在一个函数内还是不在一个js文件中。

全局(窗口)示例:

在页面的onLoad事件上执行声明windowvariables的代码,或使用浏览器控制台自行声明:

 window.Variable = true; 

然后,添加一个包含这个函数的JS文件,或者通过在浏览器控制台上执行代码来自己创build它:

 function testGlobalVar(){ if (Variable) alert("it works!"); } 

当你执行testGlobalVar()你会得到警报,但这只是因为你把它声明为`window',否则你什么也得不到。

默认修饰符:

Variable = 0.5

自动将此variables声明为Float,因为它正在接收Float值。 我不知道,如果你已经知道,但JavaScriptvariables声明为通常的方式,有一个默认的修饰符,使variables类似于windowvariables,但你不能访问它从任何你是,但在大多数情况下,你可以访问它,特别,我不知道所有的情况下,你不能访问它,但我知道你不能当你加载一个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

对你的想法感兴趣。