JavaScript 类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12610394/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 10:49:45  来源:igfitidea点击:

JavaScript Classes

javascriptoopdesign-patterns

提问by FtDRbwLXw6

I understand basic JavaScript pseudo-classes:

我了解基本的 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'

I also understand the module pattern, which can emulate encapsulation:

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

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

But there are un-OOP-like properties to both of these patterns. The former does not provide encapsulation. The latter does not provide instantiation. Both patterns can be modified to support pseudo-inheritance.

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

What I'd like to know is if there is any pattern that allows:

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

  • Inheritance
  • Encapsulation (support for "private" properties/methods)
  • Instantiation (can have multiple instances of the "class", each with its own state)
  • 遗产
  • 封装(支持“私有”属性/方法)
  • 实例化(可以有“类”的多个实例,每个实例都有自己的状态)

回答by gion_13

what about this :

那这个呢 :

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;
})();

And now we have instantiation, encapsulation and inheritance.
But, there still is a problem. The privatevariable is staticbecause it's shared across all instances of Foo. Quick demo :

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

var a = new Foo();
var b = new Foo();
a.setBar('a');
b.setBar('b');
alert(a.getBar()); // alerts 'b' :(    

A better approach might be using conventions for the private variables : any private variable should start with an underscore. This convention is well known and widely used, so when another programmer uses or alters your code and sees a variable starting with underscore, he'll know that it's private, for internal use only and he won't modify it.
Here's the rewrite using this convention :

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

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;
})();

Now we have instantiation, inheritance, but we've lost our encapsulation in favor of conventions :

现在我们有了实例化、继承,但我们已经失去了封装,而采用了约定:

var a = new Foo();
var b = new Foo();
a.setBar('a');
b.setBar('b');
alert(a.getBar()); // alerts 'a' :) 
alert(b.getBar()); // alerts 'b' :) 

but the private vars are accessible :

但是可以访问私有变量:

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

回答by Joe Davis

I think what you're looking for is the "Revealing Prototype Pattern".

我认为您正在寻找的是“揭示原型模式”。

Dan Wahlin has a great blog post: http://weblogs.asp.net/dwahlin/archive/2011/08/03/techniques-strategies-and-patterns-for-structuring-javascript-code-revealing-prototype-pattern.aspx

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

and even better Pluralsight course on this and other related JavaScript structures: http://pluralsight.com/training/courses/TableOfContents?courseName=structuring-javascript&highlight=dan-wahlin_structuring-javascript-module1!dan-wahlin_structuring-javascript-module2!dan-wahlin_structuring-javascript-module5!dan-wahlin_structuring-javascript-module4!dan-wahlin_structuring-javascript-module3#structuring-javascript-module1

甚至更好的关于这个和其他相关 JavaScript 结构的 Pluralsight 课程:http://pluralsight.com/training/courses/TableOfContents?courseName=structuring -javascript&highlight=dan-wahlin_structuring-javascript-module1!dan-wahlin_structuring-javascript-module2!dan -wahlin_structuring-javascript-module5!dan-wahlin_structuring-javascript-module4!dan-wahlin_structuring-javascript-module3#structuring-javascript-module1

回答by Stephane Catala

Closures are your friend!

闭包是你的朋友!

Simply add the following tiny function to your top-level namespace and you're ready to OOP, complete with

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

  • encapsulation, with static and instance, private and public variables and methods
  • inheritance
  • class-level injection (eg. for singleton services)
  • no constraints, no framework, just plain old Javascript
  • 封装,具有静态和实例,私有和公共变量和方法
  • 遗产
  • 类级注入(例如,用于单例服务)
  • 没有限制,没有框架,只是简单的旧 Javascript


function clazz(_class, _super) {
    var _prototype = Object.create((_super || function() {}).prototype);
    var _deps = Array.isArray(_class) ? _class : [_class]; _class = _deps.pop();
    _deps.push(_super);
    _prototype.constructor = _class.apply(_prototype, _deps) || _prototype.constructor;
    _prototype.constructor.prototype = _prototype;
    return _prototype.constructor;
}

The above function simply wires up the given class' prototype and eventual parent constructor, and returns the resulting constructor, ready for instantiation.

上面的函数简单地连接给定类的原型和最终的父构造函数,并返回结果构造函数,准备实例化。

Now you can most naturally declare your base classes (ie. that extend {}) in a few lines of code, complete with static, instance, public and private properties and methods:

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

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
    };
});

Extending a class? All the more natural as well:

扩展一个类?也更自然:

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

In other words, pass the parent class constructor to the clazz function, and add _super.call(this, arg1, ...)to the child class' constructor, which calls the parent class' constructor with the required arguments. As with any standard inheritance scheme, the parent constructor call must come first in the child constructor.

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

Note that you're free to either explicitly name the contructor with this.constructor = function(arg1, ...) {...}, or this.constructor = function MyBaseClass(arg1, ...) {...}if you need simple access to the constructor from the code inside the constructor, or even simply return the constructor with return function MyBaseClass(arg1, ...) {...}as in the above code. Whichever you feel most comfortable with.

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

Simply instantiate objects from such classes as you normally would from a constructor: myObj = new MyBaseClass();

简单地从这些类中实例化对象,就像通常从构造函数中一样: myObj = new MyBaseClass();

Notice how closures nicely encapsulate all of a class' functionality, including its prototype and constructor, providing a natural namespace for static and instance, private and public properties and methods. The code within a class closure is completely free of constraints. No framework, no constraints, just plain old Javascript. Closures rule!

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

Oh, and if you want to inject singleton dependencies (eg. services) into your class (ie. prototype), clazzwill do this for you à la AngularJS:

哦,如果您想将单例依赖项(例如服务)注入您的类(即原型),clazz将为您执行此操作,如 AngularJS:

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

As the above code attempts to illustrate, to inject singletons into a class simply place the class closure as the last entry into an array with all its dependencies. Also add the corresponding parameters to the class closure in front of the _superparameter and in the same order as in the array. clazzwill inject the dependencies from the array as arguments into the class closure. The dependencies are then available anywhere within the class closure, including the constructor.

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

In fact, since the dependencies are injected into the prototype, they are available to static methods even before any object is instantiated from the class. This is very powerful for wiring up your apps or unit and end-to-end tests. It also removes the need to inject singletons into constructors, which otherwise unnecessarily clobbers the constructor's code.

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

Check this fiddle: http://jsfiddle.net/5uzmyvdq/1/

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

Feedback and suggestions most welcome!

非常欢迎反馈和建议!

回答by user1701047

Javascript is certainly OOP. You always have polymorphism, however you have to sacrifice either encapsulation or instantiation which is the problem you ran into.

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

Try this to just brush up on your options. http://www.webmonkey.com/2010/02/make_oop_classes_in_javascript/Also an old question that I had bookmarked: Is JavaScript object-oriented?

试试这个只是复习你的选择。 http://www.webmonkey.com/2010/02/make_oop_classes_in_javascript/还有一个我收藏的老问题: JavaScript 是面向对象的吗?

回答by user1701047

JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance.

JavaScript 类是在 ECMAScript 6 中引入的,是 JavaScript 现有的基于原型的继承的语法糖。类语法并没有向 JavaScript 引入一个新的面向对象的继承模型。JavaScript 类提供了一种更简单、更清晰的语法来创建对象和处理继承。

You can see more in this link Mozilla Community

您可以在此链接中查看更多信息Mozilla 社区

Github

GitHub

回答by Piacenti

One problem with a lot of JS classes out there is that they do not secure their fields and methods which means that anyone using it may accidentally replace a method. For example the code:

许多 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();

will output:

将输出:

ugly
BOSS
replaced 
replaced 

As you can see the changeName function gets overriden. The following code would secure the class methods and fields and the getters and setters would be used to access them making this more of a "regular" class found in other languages.

如您所见,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();

This outputs:

这输出:

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

Now your class methods cannot be replaced by random values or functions and the code in the getters and setters always run when attempting to read or write to field.

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

回答by BrettJephson

I was thinking about this particular subject recently and the limitations of the various approaches. The best solution I've been able to come up with is below.

我最近在考虑这个特定主题以及各种方法的局限性。我能想出的最佳解决方案如下。

It seems to solve the problems with inheritance, instantiation and ecapsulation (at least from tests on Google Chrome v.24) although probably at a cost in memory usage.

它似乎解决了继承、实例化和封装的问题(至少从 Google Chrome v.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();

回答by jdbence

This closure allows instantiation and encapsulation but no inheritance.

这个闭包允许实例化和封装,但不允许继承。

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"