JavaScript类

FtD*_*Xw6 45 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'
Run Code Online (Sandbox Code Playgroud)

我也理解模块模式,它可以模拟封装:

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
Run Code Online (Sandbox Code Playgroud)

但是这两种模式都有类似OOP的属性.前者不提供封装.后者不提供实例化.可以修改这两种模式以支持伪继承.

我想知道的是,是否有任何模式允许:

  • 遗产
  • 封装(支持"私有"属性/方法)
  • 实例化(可以有多个"类"实例,每个实例都有自己的状态)

gio*_*_13 72

那这个呢 :

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;
})();
Run Code Online (Sandbox Code Playgroud)

现在我们有实例化,封装和继承.
但是,仍然存在问题.该private变量是static因为它跨越的所有实例共享Foo.快速演示:

var a = new Foo();
var b = new Foo();
a.setBar('a');
b.setBar('b');
alert(a.getBar()); // alerts 'b' :(    
Run Code Online (Sandbox Code Playgroud)

更好的方法可能是使用私有变量的约定:任何私有变量都应该以下划线开头.这个约定是众所周知的并且被广泛使用,所以当另一个程序员使用或改变你的代码并看到以下划线开头的变量时,他会知道它是私有的,仅供内部使用,他不会修改它.
这是使用此约定的重写:

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;
})();
Run Code Online (Sandbox Code Playgroud)

现在我们有实例化,继承,但是我们已经失去了封装以支持约定:

var a = new Foo();
var b = new Foo();
a.setBar('a');
b.setBar('b');
alert(a.getBar()); // alerts 'a' :) 
alert(b.getBar()); // alerts 'b' :) 
Run Code Online (Sandbox Code Playgroud)

但私人诉讼是可以访问的:

delete a._bar;
b._bar = null;
alert(a.getBar()); // alerts undefined :(
alert(b.getBar()); // alerts null :(
Run Code Online (Sandbox Code Playgroud)

  • 此处的私有状态将在类的所有实例之间共享。您无法使用此代码创建两个具有不同状态的实例。 (2认同)

Joe*_*vis 7

我认为你所寻找的是"揭示原型模式".

Dan Wahlin有一篇很棒的博客文章:http://weblogs.asp.net/dwahlin/archive/2011/08/03/techniques-strategies-and-patterns-for-structuring-javascript-code-revealing-prototype-pattern . ASPX

甚至更好的Pluralsight关于这个和其他相关的JavaScript结构的课程:http://pluralsight.com/training/courses/TableOfContents?courseName = struct -javascript&highlight/= light -wahlin_structuring-javascript-module1!dan -wahlin_structuring -javascript-module2!dan -wahlin_structuring-JavaScript的单词数!丹wahlin_structuring-JavaScript的单词数!丹wahlin_structuring-JavaScript的单词数#结构,JavaScript的模块1


Ste*_*ala 5

闭包是你的朋友!

\n\n

只需将以下小函数添加到您的顶级命名空间中,您就可以进行 OOP,完成:

\n\n
    \n
  • 封装,具有静态和实例、私有和公共变量\n以及方法
  • \n
  • 遗产
  • \n
  • 类级注入(例如,对于单例服务)
  • \n
  • 没有限制,没有框架,只是普通的旧 Javascript
  • \n
\n\n
\n\n
function clazz(_class, _super) {\n    var _prototype = Object.create((_super || function() {}).prototype);\n    var _deps = Array.isArray(_class) ? _class : [_class]; _class = _deps.pop();\n    _deps.push(_super);\n    _prototype.constructor = _class.apply(_prototype, _deps) || _prototype.constructor;\n    _prototype.constructor.prototype = _prototype;\n    return _prototype.constructor;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

上面的函数只是连接给定类的原型和最终的父构造函数,并返回生成的构造函数,准备实例化。

\n\n

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

\n\n
MyBaseClass = clazz(function(_super) { // class closure, \'this\' is the prototype\n    // local variables and functions declared here are private static variables and methods\n    // properties of \'this\' declared here are public static variables and methods\n    return function MyBaseClass(arg1, ...) { // or: this.constructor = function(arg1, ...) {\n        // local variables and functions declared here are private instance variables and methods\n        // properties of \'this\' declared here are public instance variables and methods\n    };\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

延长班级?一切也更加自然:

\n\n
MySubClass = clazz(function(_super) { // class closure, \'this\' is the prototype\n    // local variables and functions are private static variables and methods\n    // properties of this are public static variables and methods\n    return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {\n        // local variables and functions are private instance variables and methods\n        _super.apply(this, arguments); // or _super.call(this, arg1, ...)\n        // properties of \'this\' are public instance variables and methods\n    };\n}, MyBaseClass); // extend MyBaseClass\n
Run Code Online (Sandbox Code Playgroud)\n\n

换句话说,将父类构造函数传递给 clazz 函数,并添加_super.call(this, arg1, ...)到子类的构造函数,子类的构造函数将使用所需的参数调用父类的构造函数。与任何标准继承方案一样,父构造函数调用必须首先出现在子构造函数中。

\n\n

请注意,您可以使用 显式命名构造函数this.constructor = function(arg1, ...) {...},或者this.constructor = function MyBaseClass(arg1, ...) {...}如果您需要从构造函数内部的代码简单访问构造函数,甚至只需使用return function MyBaseClass(arg1, ...) {...}如上面的代码所示。无论你觉得哪个最舒服。

\n\n

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

\n\n

请注意闭包如何很好地封装类的所有功能,包括其原型和构造函数,为静态和实例、私有和公共属性和方法提供自然的命名空间。类闭包内的代码完全不受约束。没有框架,没有限制,只有普通的旧 Javascript。关闭规则!

\n\n

哦,如果你想将单例依赖项(例如服务)注入到你的类(即原型)中,clazz将为你执行此操作 \xc3\xa0 la AngularJS:

\n\n
DependentClass = clazz([aService, function(_service, _super) { // class closure, \'this\' is the prototype\n    // the injected _service dependency is available anywhere in this class\n    return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {\n        _super.apply(this, arguments); // or _super.call(this, arg1, ...)\n        // the injected _service dependency is also available in the constructor\n    };\n}], MyBaseClass); // extend MyBaseClass\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如上面的代码试图说明的,要将单例注入到类中,只需将类闭包作为最后一个条目放入带有其所有依赖项的数组中。还将相应的参数添加到类闭包中的参数前面_super,并且顺序与数组中的顺序相同。clazz会将数组中的依赖项作为参数注入到类闭包中。然后,依赖项在类闭包内的任何位置都可用,包括构造函数。

\n\n

事实上,由于依赖项被注入到原型中,因此即使在从类实例化任何对象之前,静态方法也可以使用它们。这对于连接应用程序或单元和端到端测试非常强大。它还消除了将单例注入构造函数的需要,否则会不必要地破坏构造函数的代码。

\n\n

检查这个小提琴: http: //jsfiddle.net/5uzmyvdq/1/

\n\n

欢迎反馈和建议!

\n