无法在 javascript 中拥有基于类的对象? [英] No ways to have class-based objects in javascript?

查看:25
本文介绍了无法在 javascript 中拥有基于类的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基于 javascript 原型的面向对象编程风格很有趣,但在很多情况下,您需要能够从类创建对象.

The javascript prototype-based object-oriented programming style is interesting, but there are a lot of situations where you need the ability to create objects from a class.

例如在矢量绘图应用程序中,工作区通常在绘图开始时是空的:我无法从现有的线"创建新的线".更一般地说,动态创建对象的每种情况都需要使用类.

For instance in a vector drawing application, the workspace will usually be empty at the beginning of the drawing : I cannot create a new "line" from an existing one. More generally, every situation where objects are being dynamically created require the use of classes.

我已经阅读了很多教程和Javascript:好的部分"一书,但在我看来,没有办法定义尊重 1) 封装和 2) 高效成员方法声明的类(我意思是:成员方法被定义一次,并在每个类实例之间共享).

I've read a lot of tutorials and the book "Javascript : the good parts", but yet it seems to me that there is no way to define classes that respect 1) encapsulation and 2) efficient member methods declaration (I mean : member methods that are being defined once, and shared among every class instances).

为了定义私有变量,使用了闭包:

To define private variables, closures are being used :

function ClassA()
{
    var value = 1;
    this.getValue = function()
    {
        return value;
    }
}

这里的问题是ClassA"的每个实例都会有自己的成员函数getValue"的副本,这样效率不高.

The problem here is that every instance of "ClassA" will have its own copy of the member function "getValue", which is not efficient.

为了有效地定义成员函数,使用原型:

To define member functions efficiently, prototype is being used :

function ClassB()
{
    this.value = 1;
}

ClassB.prototype.getValue = function()
{
    return this.value;
}

这里的问题是成员变量value"是公开的.

The problem here is that the member variable "value" is public.

我认为这个问题不容易解决,因为需要在对象创建期间定义私有"变量(以便对象可以访问其创建上下文,而不会暴露这些值)而原型-基于成员函数的定义必须在对象创建之后完成,这样原型才有意义(this.prototype"不存在,我已经检查过).

I don't think that this issue can be solved easily, since "private" variables need to be defined DURING object creation (so that the object can have access to its context of creation, without exposing thoses values) whereas prototype-based member functions definition has to be done AFTER object creation, so that prototype makes sense ("this.prototype" does not exists, I've checked).

还是我遗漏了什么?

首先,感谢您的有趣回答.

First of all, thank you for your interesting answers.

我只是想为我的初始信息添加一点精确度:

I just wanted to add a little precision to my initial message :

我真正想做的是拥有 1) 私有变量(封装很好,因为人们只能访问他们需要的东西)和 2) 高效的成员方法声明(避免复制).

What I really want to do is to have 1) private variables (encapsulation is good, because people only have access to what they need) and 2) efficient member methods declaration (avoid copies).

似乎简单的私有变量声明实际上只能通过 javascript 中的闭包来实现,这就是我关注基于类的方法的根本原因.如果有一种方法可以使用基于原型的方法来实现简单的私有变量声明,那对我来说没问题,我不是一个激烈的基于类的方法支持者.

It seems that simple private variables declaration can really only be achieved via closure in javascript, that's essentially why I focused on the class based approach. If there is a way to achieve simple private variables declaration with a prototype based approach, that's okay for me, I'm not a fierce class-based approach proponnent.

阅读答案后,似乎简单的解决方案是忘记私有,并使用特殊的编码约定来阻止其他程序员直接访问私有"变量...

After reading the answers, it seems like the simple solution is to forget about privates, and use a special coding conventions to detter other programmers from accessing "private" variables directly...

我同意,我的标题/第一句话对我想在这里讨论的问题具有误导性.

And I agree, my title / first sentence was misleading regarding the issue I wanted to discuss here.

推荐答案

嘘,过来!想听个秘密吗?

经典继承是一种经过测试和尝试的方法.

Shh, come here! Wanna hear a secret?

Classical inheritance is a tested and tried approach.

经常在 JavaScript 中实现它很有用.类是一个很好的概念,并且拥有在对象之后对我们的世界进行建模的模板很棒.

It is useful to implement it in JavaScript often. Classes are a nice concept to have and having templates for modeling our world after objects is awesome.

原型继承侧重于共享功能,这很棒(恐龙鼓槌很棒),但在某些情况下,您希望共享数据方案而不是功能.这是原型继承根本无法解决的问题.

Prototypical inheritance focuses on sharing functionality and that's awesome (dinasaur drumstick awesome), but in some cases you want to share a data-scheme and not functionality. That's a problem prototypical inheritance does not address at all.

不,他们不是.JS 社区不赞成的不是类的概念,而是将自己限制在仅用于代码重用的类.就像语言不强制强类型或静态类型一样,它也不强制对象结构的方案.

No, they are not. What the JS community frowns upon is not the concept of classes, it's limiting yourself to just classes for code reuse. Just like the language does not enforce strong or static typing, it doesn't enforce schemes on object structure.

事实上,语言的幕后巧妙实现可以将你的普通对象变成类似于经典继承类的东西.

In fact, behind the scene clever implementations of the language can turn your normal objects to something resembling classical inheritance classes.

嗯,你真的只需要一个构造函数:

Well, you really only need a constructor:

function getVehicle(engine){
    return { engine : engine };
}

var v = getVehicle("V6");
v.engine;//v6

我们现在有一个车辆类.我们不需要使用特殊关键字显式定义 Vehicle 类.现在,有些人不喜欢以这种方式做事,而是习惯了更经典的方式.为此 JS 提供(愚蠢的恕我直言)语法糖:

We now have a vehicle class. We didn't need to define a Vehicle class explicitly using a special keyword. Now, some people don't like to do things this way and are used to the more classical way. For this JS provides (silly imho) syntactic sugar by doing:

function Vehicle(engine){
     this.engine = engine;
}
var v = new Vehicle("V6");
v.engine;//v6

大多数情况下,这与上面的示例相同.

That's the same thing as the example above for the most part.

那么,我们还缺少什么?

So, what are we still missing?

如果我告诉你基本的子类型在 JavaScript 中非常简单呢?

What if I told you basic subtyping is very simple in JavaScript?

JavaScript 的类型概念与我们在其他语言中习惯的不同.在JS中成为某种类型的子类型是什么意思?

JavaScript's notion of typing is different than what we're used to in other languages. What does it mean to be a sub-type of some type in JS?

var a = {x:5};
var b = {x:3,y:3};

b 的类型是 a 类型的子类型吗?假设它是根据(强)行为子类型(LSP):

Is the type of b a sub type of the type of a? Let's say if it is according to (strong) behavioral subtyping (the LSP):

<<<<<<开始技术部分

  • 逆变 中的方法参数子类型 - 在这种继承中完全保留.
  • 协方差子类型 - 完全保留在这种继承中.
  • 子类型的方法不应抛出新的异常,除非这些异常本身是超类型方法抛出的异常的子类型.- 完全保留在这种继承中.

还有,

  • Preconditions cannot be strengthened in a subtype.
  • Postconditions cannot be weakened in a subtype.
  • Invariants of the supertype must be preserved in a subtype.
  • The history rule

所有这些都是再一次,由我们来保留.我们可以随心所欲地保持它们紧密或松散,我们不必这样做,但我们肯定可以.

All of these are again, are up to us to keep. We can keep them as tightly or loosly as we want, we don't have to, but we surely can.

所以事实上,只要我们在实现继承时遵守上述这些规则,我们就完全实现了强行为子类型,这是一种非常强大的子类型(见注释*).

So matter of fact, as long as we abide to these rules above when implementing our inheritance, we're fully implementing strong behavioral subtyping, which is a very powerful form of subtyping (see note*).

>>>>> 技术部分结束

简单地说,人们也可以看到结构子类型成立.

Trivially, one can also see that structural subtyping holds.

function getCar(typeOfCar){
    var v = getVehicle("CarEngine");
    v.typeOfCar = typeOfCar;
    return v;
}
v = getCar("Honda");
v.typeOfCar;//Honda;
v.engine;//CarEngine

不会太难吧?私人会员呢?

Not too hard, was it? What about private members?

function getVehicle(engine){
    var secret = "Hello"
    return {
        engine : engine,
        getSecret : function() {
            return secret;
        }
    };
}

看,secret 是一个 closure 变量.它是完全私有的",它的工作方式与 Java 等语言中的私有不同,但无法从外部访问.

See, secret is a closure variable. It's perfectly "private", it works differently than privates in languages like Java, but it's impossible to access from the outside.

啊!这是一个很好的问题.

Ah! That's a great question.

如果我们想在原型上共享的函数中使用私有变量,我们需要首先了解 JS 闭包和函数的工作原理.

If we want to use a private variable in a function we share on the prototype we need to firrst understand how JS closures and functions work.

在 JavaScript 中,函数是一流的.这意味着您可以传递函数.

In JavaScript functions are first class. This means you can pass functions around.

function getPerson(name){
    var greeting = "Hello " + name;
    return {
        greet : function() {
            return greeting;
        }
    };
}

var a = getPerson("thomasc");
a.greet(); //Hello thomasc

到目前为止一切顺利,但我们可以将绑定到 a 的函数传递给其他对象!这让您可以进行非常松散的解耦,这非常棒.

So far so good, but we can pass that function bounded to a around to other objects! This lets you do very loose decoupling which is awesome.

var b = a.greet;
b(); //Hello thomasc

等等!b 怎么知道这个人的名字是 thomasc?这就是闭包的神奇之处.非常棒吧?

Wait! How did b know the person's name is thomasc? That's just the magic of closures. Pretty awesome huh?

您可能会担心性能问题.让我告诉你我是如何学会不再担心并开始喜欢优化 JIT 的.

You might be worried about performance. Let me tell you how I learned to stop worrying and started to love the optimizing JIT.

实际上,拥有这样的函数副本并不是什么大问题.javascript 中的函数都是关于功能的!闭包是一个很棒的概念,一旦你掌握并掌握了它们,你就会发现它是非常值得的,而对性能的影响真的没有那么重要.JS 一天比一天快,不用担心这些性能问题.

In practice, having copies of functions like that is not a big issue. Functions in javascript are all about well, functionality! Closures are an awesome concept, once you grasp and master them you see it's well worth it, and the performance hit really isn't that meaningful. JS is getting faster every day, don't worry about these sort of performance issues.

如果你觉得很复杂,下面的也很合理.与其他开发人员的共同合同只是简单地说如果我的变量以 _ 开头,请不要碰它,我们都是同意的成年人".这看起来像:

If you think it's complicated, the following is also very legitimate. A common contract with other developers simply says "If my variable starts with _ don't touch it, we are both consenting adults". This would look something like:

function getPerson(name){
    var greeter = {
        greet : function() {
            return "Hello" +greeter._name;
        }
    };
    greeter._name = name;
    return greeter;
}

或者古典风格

function Person(name){
    this._name = name;
    this.greet = function(){
       return "Hello "+this._name;
    }
}

或者,如果您想在原型上缓存函数而不是实例化副本:

Or if you'd like to cache the function on the prototype instead of instantiate copies:

function Person(name){
    this._name = name;
}
Person.prototype.greet =  function(){
       return "Hello "+this._name;
}

所以,总结一下:

  • 您可以使用经典的继承模式,它们对于共享数据类型很有用

    So, to sum it up:

    • You can use classical inheritance patterns, they are useful for sharing types of data

      您还应该使用原型继承,它同样有效,而且在您想要共享功能的情况下还可以使用更多.

      You should also use prototypical inheritance, it is just as potent, and much more in cases you want to share functionality.

      TheifMaster 漂亮非常成功.在 JavaScript 中,私有私有确实不是什么大问题,只要您的代码定义了一个清晰的接口,这根本就不会有问题.我们都是成年人 :)

      TheifMaster pretty much nailed it. Having privates private is really not a big deal as one might think in JavaScript, as long as your code defines a clear interface this should not be problematic at all. We're all concenting adults here :)

      *聪明的读者可能会想:嗯?你不是在用历史规则欺骗我吗?我的意思是,属性访问没有被封装.

      <子>我说不,我不是.即使您没有明确地将字段封装为私有,您也可以简单地以不访问它们的方式定义您的合同.通常就像 TheifMaster 用 _ 建议的那样.另外,我认为历史规则在很多这样的场景中并不是什么大问题,只要我们不改变属性访问处理父对象属性的方式.再次,这取决于我们.

      I say no, I was not. Even if you don't explicitly encapsulate the fields as private, you can simply define your contract in a way that does not access them. Often like TheifMaster suggested with _. Also, I think the history rule is not that big of a deal in a lot of such scenarios as long as we're not changing the way property access treats properties of the parent object. Again, it's up to us.

      这篇关于无法在 javascript 中拥有基于类的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆