Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/420.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaScript类_Javascript_Oop_Design Patterns - Fatal编程技术网

JavaScript类

JavaScript类,javascript,oop,design-patterns,Javascript,Oop,Design Patterns,我了解基本的JavaScript伪类: function Foo(bar) { this._bar = bar; } Foo.prototype.getBar = function() { return this._bar; }; var foo = new Foo('bar'); alert(foo.getBar()); // 'bar' alert(foo._bar); // 'bar' 我还了解模块模式,它可以模拟封装: var Foo = (function() {

我了解基本的JavaScript伪类:

function Foo(bar) {
    this._bar = bar;
}

Foo.prototype.getBar = function() {
    return this._bar;
};

var foo = new Foo('bar');
alert(foo.getBar()); // 'bar'
alert(foo._bar); // 'bar'
我还了解模块模式,它可以模拟封装:

var Foo = (function() {
    var _bar;

    return {
        getBar: function() {
            return _bar;
        },
        setBar: function(bar) {
            _bar = bar;
        }
    };
})();

Foo.setBar('bar');
alert(Foo.getBar()); // 'bar'
alert(Foo._bar); // undefined
但这两种模式都有类似于OOP的属性。前者不提供封装。后者不提供实例化。这两种模式都可以修改以支持伪继承

function Foo(){
    var _bar = "foo";

    return {
        getBar: function() {
            return _bar;
        },
        setBar: function(bar) {
            _bar = bar;
        }
    };
};

a = Foo();
b = Foo();

a.setBar("bar");
alert(a.getBar()); // "bar"
alert(b.getBar()); // "foo"
我想知道的是,是否有任何模式允许:

  • 继承权
  • 封装(支持“私有”属性/方法)
  • 实例化(可以有多个“类”实例,每个实例都有自己的状态)

    • Javascript当然是面向对象的。您总是有多态性,但是您必须牺牲封装或实例化,这是您遇到的问题

      试试这个,让你的选择更加清晰。 还有一个我已经加入书签的老问题:

      我想你要找的是“揭示原型模式”

      Dan Wahlin有一篇很棒的博文:

      还有关于这个和其他相关JavaScript结构的更好的Pluralsight课程:

      这个呢:

      var Foo = (function() {
          // "private" variables 
          var _bar;
      
          // constructor
          function Foo() {};
      
          // add the methods to the prototype so that all of the 
          // Foo instances can access the private static
          Foo.prototype.getBar = function() {
              return _bar;
          };
          Foo.prototype.setBar = function(bar) {
              _bar = bar;
          };
      
          return Foo;
      })();
      
      现在我们有了实例化、封装和继承。
      但是,仍然存在一个问题。
      private
      变量是
      static
      ,因为它在
      Foo
      的所有实例中共享。快速演示:

      var a = new Foo();
      var b = new Foo();
      a.setBar('a');
      b.setBar('b');
      alert(a.getBar()); // alerts 'b' :(    
      
      更好的方法可能是对私有变量使用约定:任何私有变量都应该以下划线开头。这种约定是众所周知且广泛使用的,因此当其他程序员使用或修改您的代码时,如果看到以下划线开头的变量,他将知道该变量是私有的,仅供内部使用,他不会修改它。
      以下是使用此约定进行的重写:

      var Foo = (function() {
          // constructor
          function Foo() {
              this._bar = "some value";
          };
      
          // add the methods to the prototype so that all of the 
          // Foo instances can access the private static
          Foo.prototype.getBar = function() {
              return this._bar;
          };
          Foo.prototype.setBar = function(bar) {
              this._bar = bar;
          };
      
          return Foo;
      })();
      
      现在我们有了实例化、继承,但我们已经失去了封装,转而支持约定:

      var a = new Foo();
      var b = new Foo();
      a.setBar('a');
      b.setBar('b');
      alert(a.getBar()); // alerts 'a' :) 
      alert(b.getBar()); // alerts 'b' :) 
      
      但私人VAR是可访问的:

      delete a._bar;
      b._bar = null;
      alert(a.getBar()); // alerts undefined :(
      alert(b.getBar()); // alerts null :(
      

      我最近一直在思考这个问题,以及各种方法的局限性。我能想出的最好的解决方案如下

      它似乎解决了继承、实例化和ecapsulation的问题(至少在GoogleChromeV.24上的测试中是这样),尽管可能会以内存使用为代价

      function ParentClass(instanceProperty) {
        // private
        var _super = Object.create(null),
            privateProperty = "private " + instanceProperty;
        // public
        var api = Object.create(_super);
        api.constructor = this.constructor;
        api.publicMethod = function() {
           console.log( "publicMethod on ParentClass" );
           console.log( privateProperty );
        };
        api.publicMethod2 = function() {
           console.log( "publicMethod2 on ParentClass" );
           console.log( privateProperty );
        };
        return api;
      }
      
      function SubClass(instanceProperty) {
          // private
          var _super = ParentClass.call( this, instanceProperty ),
              privateProperty = "private sub " + instanceProperty;
          // public
          var api = Object.create(_super);
          api.constructor = this.constructor;
          api.publicMethod = function() {
             _super.publicMethod.call(this); // call method on ParentClass
              console.log( "publicMethod on SubClass" );
              console.log( privateProperty );
          }
          return api;
      }
      
      var par1 = new ParentClass(0),
          par2 = new ParentClass(1),
          sub1 = new SubClass(2),
          sub2 = new SubClass(3);
      
      par1.publicMethod();
      par2.publicMethod();
      sub1.publicMethod();
      sub2.publicMethod();
      par1.publicMethod2();
      par2.publicMethod2();
      sub1.publicMethod2();
      sub2.publicMethod2();
      

      此闭包允许实例化和封装,但不允许继承

      function Foo(){
          var _bar = "foo";
      
          return {
              getBar: function() {
                  return _bar;
              },
              setBar: function(bar) {
                  _bar = bar;
              }
          };
      };
      
      a = Foo();
      b = Foo();
      
      a.setBar("bar");
      alert(a.getBar()); // "bar"
      alert(b.getBar()); // "foo"
      

      很多JS类的一个问题是它们没有保护它们的字段和方法,这意味着任何使用它的人都可能意外地替换一个方法。例如,代码:

      function Class(){
          var name="Luis";
          var lName="Potter";
      }
      
      Class.prototype.changeName=function(){
          this.name="BOSS";
          console.log(this.name);
      };
      
      var test= new Class();
      console.log(test.name);
      test.name="ugly";
      console.log(test.name);
      test.changeName();
      test.changeName=function(){
          console.log("replaced");
      };
      test.changeName();
      test.changeName();
      
      将输出:

      ugly
      BOSS
      replaced 
      replaced 
      
      正如您所看到的,changeName函数被覆盖。下面的代码将保护类方法和字段,并使用getter和setter来访问它们,从而使其成为其他语言中的“常规”类

      function Class(){
          var name="Luis";
          var lName="Potter";
      
          function getName(){
               console.log("called getter"); 
               return name;
          };
      
          function setName(val){
               console.log("called setter"); 
               name = val
          };
      
          function getLName(){
               return lName
          };
      
          function setLName(val){
              lName = val;
          };
      
          Object.defineProperties(this,{
              name:{
                  get:getName, 
                  set:setName, 
                  enumerable:true, 
                  configurable:false
              },
              lastName:{
                  get:getLName, 
                  set:setLName, 
                  enumerable:true, 
                  configurable:false
              }
          });
      }
      
      Class.prototype.changeName=function(){
          this.name="BOSS";
      };   
      
      Object.defineProperty(Class.prototype, "changeName", {
          writable:false, 
          configurable:false
      });
      
      var test= new Class();
      console.log(test.name);
      test.name="ugly";
      console.log(test.name);
      test.changeName();
      test.changeName=function(){
          console.log("replaced")
      };
      test.changeName();
      test.changeName();
      
      这将产生:

      called getter
      Luis
      called setter 
      called getter 
      ugly 
      called setter 
      called setter 
      called setter 
      

      现在,您的类方法不能被随机值或函数替换,getter和setter中的代码总是在尝试读取或写入字段时运行。

      闭包是您的朋友

      只需将以下小函数添加到顶级名称空间,就可以进行OOP,并完成以下操作

      • 封装,带有静态和实例、私有和公共变量 方法
      • 继承权
      • 类级注入(例如针对单例服务)
      • 没有约束,没有框架,只有简单的老Javascript

      上面的函数只是将给定类的原型和最终的父构造函数连接起来,并返回生成的构造函数,以便实例化

      现在,您可以在几行代码中最自然地声明基类(即扩展{}),包括静态、实例、公共和私有属性和方法:

      MyBaseClass = clazz(function(_super) { // class closure, 'this' is the prototype
          // local variables and functions declared here are private static variables and methods
          // properties of 'this' declared here are public static variables and methods
          return function MyBaseClass(arg1, ...) { // or: this.constructor = function(arg1, ...) {
              // local variables and functions declared here are private instance variables and methods
              // properties of 'this' declared here are public instance variables and methods
          };
      });
      
      扩展一个类?更自然的是:

      MySubClass = clazz(function(_super) { // class closure, 'this' is the prototype
          // local variables and functions are private static variables and methods
          // properties of this are public static variables and methods
          return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {
              // local variables and functions are private instance variables and methods
              _super.apply(this, arguments); // or _super.call(this, arg1, ...)
              // properties of 'this' are public instance variables and methods
          };
      }, MyBaseClass); // extend MyBaseClass
      
      换句话说,将父类构造函数传递给clazz函数,并将
      \u super.call(this,arg1,…)
      添加到子类的构造函数,该构造函数使用所需参数调用父类的构造函数。与任何标准继承方案一样,父构造函数调用必须位于子构造函数的第一位

      请注意,如果需要从构造函数内部的代码简单访问构造函数,您可以使用
      this.constructor=function(arg1,…{…}
      this.constructor=function MyBaseClass(arg1,…{…}
      显式命名构造函数,或者甚至像上面的代码一样,使用返回函数MyBaseClass(arg1,…{…}返回构造函数。你觉得最舒服的

      只需像通常从构造函数中一样从此类类实例化对象:
      myObj=newmybaseclass()

      注意闭包是如何很好地封装类的所有功能的,包括它的原型和构造函数,为静态和实例、私有和公共属性和方法提供了一个自然的名称空间。类闭包中的代码完全没有约束。没有框架,没有约束,只有简单的老Javascript。关闭规则

      哦,如果您想将单例依赖项(如服务)注入到您的类(即原型)中,
      clazz
      将为您实现这一点

      DependentClass = clazz([aService, function(_service, _super) { // class closure, 'this' is the prototype
          // the injected _service dependency is available anywhere in this class
          return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {
              _super.apply(this, arguments); // or _super.call(this, arg1, ...)
              // the injected _service dependency is also available in the constructor
          };
      }], MyBaseClass); // extend MyBaseClass
      
      正如上面的代码试图说明的那样,要将单例注入到类中,只需将类闭包作为最后一个条目放入具有所有依赖项的数组中。还将相应的参数添加到
      \u super
      参数前面的类闭包中,顺序与数组中的顺序相同
      clazz
      将把数组中的依赖项作为参数注入到类闭包中。然后,依赖项在类闭包中的任何位置都可用,包括构造函数

      事实上,由于依赖项被注入到原型中,所以即使在从类实例化任何对象之前,静态方法也可以使用它们。这是非常强大的接线您的应用程序或单位,并结束了en