Skip to content
This repository

Simple types instead of classes #8

Open
nzakas opened this Issue May 07, 2012 · 67 comments
Nicholas C. Zakas
nzakas commented May 07, 2012

I don't think JavaScript needs classes at all, what it does need is a simpler way of creating custom types that doesn't involve multiple steps. Right now you need to do this:

function MyType() {
    this.property = "foo";
}

MyType.prototype = {
    constructor: MyType,
    method: function() {
        // code
    };
};

Instead, I'd like to see something like this:

var MyType  = new Type({
    constructor: function() {
        this.property = "foo";
    },
    method: function() {

    }
});

This would just be sugar for creating a new type and falls inline with upcoming StructType and ArrayType. It can even be written today in JavaScript:

function Type(details){
    details.constructor.prototype = details;
    return details.constructor;
}

Want to inherit? How about:

var MyType  = new Type(SuperType, {
    constructor: function() {
        this.property = "foo";
    },
    method: function() {

    }
});

var foo = new MyType();

Easy, painless, without introducing new syntax you've removed the horror of trying to create custom types.

More:
http://www.nczonline.net/blog/2011/11/04/custom-types-classes-using-object-literals-in-javascript/

VOTE: http://goo.gl/mod/FIar

Sasha Chedygov

Completely agree. Even though this is something that can be (and has been) done currently, I think we need a standardized way of doing it.

angus croll
Owner
angus-c commented May 07, 2012

I like this. Its all we need IMO. Does anyone want the ability to access super.method? (I don't FWIW)

Anton Kovalyov
Owner
valueof commented May 07, 2012

A lot of Backbone code (ours included) uses super-methods with their current ugly form: ParentModel.prototype.myMethod.call(this).

Juan Ignacio Dopazo

The point of a class syntax is having one nice way of writing reusable templates. We already have 3000 different implementations of classes like this.

// Mootools
var Cat = new Class({
    initialize: function(name){
        this.name = name;
    }
});

// Prototype
var Cat = Class.create({
  initialize: function(name) {
    this.name = name;
  }
});

// YUI
var Cat = Y.Base.create('cat', null, [], {
  initializer: function(name) {
    this.name = name;
  }
});

// dojo
var Cat = declare(null, {
  constructor: function(name){
    this.name = name;
  }
});

Why add another one just like the ones we already have? Of course JS doesn't need classes. But what's wrong with adding them?

And yes, super would be really useful.

Rick Waldron

How is...

var MyType  = new Type({
    constructor: function() {
        this.property = "foo";
    },
    method: function() {

    }
});

Any better then...

class MyType {

  constructor() {
    this.property = "foo";
  },
  method() {

  }
};

...which literally produces a MyType constructor that has a prototype with one method called method... in an incredibly smaller amount of code/keystrokes.

Martin Kadlec

It is better because it does not introduce new syntax.

Anton Kovalyov
Owner
valueof commented May 07, 2012

And is shim-able. (not taking sides though)

Colin Snover
csnover commented May 07, 2012

So this proposal is basically standardizing an interface for something like Dojo’s declare function? I like standard interfaces but I am not sure why this isn’t something the scriptlib-cg couldn’t do if people decided to actually…work on it.

angus croll
Owner
angus-c commented May 07, 2012
angus croll
Owner
angus-c commented May 07, 2012

I'll be honest my first choice is to do nothing. No classes, no sugar. Leave it all alone. But if we MUST have sugar lets do it in a minimally invasive way.

Rick Waldron

It's a good thing we all blindly hate new syntax.

-9001

Anton Kovalyov
Owner
valueof commented May 07, 2012

I don't think it's blind. You have to be very careful with any new syntactic changes because every such change pushes the date when we are finally able to enjoy it further into the future. When will ES6 be finished? Next year? With @nzakas's solution we can shim it and start using right away (or even before). With new syntax most web developers will have to wait until all modern browsers implement that syntax. And if you're unfortunate 72%[1] you're shit out of luck because Microsoft will never update their older browsers. For such an obvious pain point (I disagree with @angus-c here; classes/types is a very important pain point) the default class solution is far for ideal.

UPDATE: I understand that we could all use transpilers for that but switching all the workflows in the world to use one transpiler also takes time. It's hard enough to convince people/companies to lint their code—I don't even want to think about having them compile their JavaScript. They might as well use something like CoffeeScript or Dart at this point.

[1] — http://gopollgo.com/what-browsers-do-you-test-your-sites-slash-apps-against

Jordan Harband
ljharb commented May 07, 2012

GitHub needs a +1 button. I strongly agree with @antonkovalyov and @angus-c - first choice, do nothing. second choice, do something shim-able. If we can't add it with a shim, we're basically forking the language since it will be forever until we can all use that natively on the client side.

Anton Kovalyov
Owner
valueof commented May 07, 2012

@ljharb I will setup a cross-linked Google Moderator later today.

Rick Waldron

"shimmable"? The "shim" is to desugar to what exists today - the shim already exists

Juan Ignacio Dopazo

I don't think any shimmable solution solves the problem. It just adds another class style to the ecosystem. I think there are two different problems here: having (or not) dedicated syntax in the long term, and helping developers deal with prototypes in the short-medium term.

The first point is not about code size. It's about readability and common ground. And the problem is finding the right balance between adding more surface to the language, more constructs to learn, and having code that means what it reads.

The second point can be addressed with a proposal like this one. But in my opinion new Type isn't a good choice. "Type" means very a very different thing in JavaScript and the difference is worse in other languages. Besides, it feels akward to me to have to write new to get a constructor which I'll have to call new on again to get an instance. I'd prefer something like Function.prototype.extend.

// Just the basic idea
Function.prototype.extend = function (proto) {
    var constructor = proto.constructor;
    constructor.prototype = Object.create(this.prototype, Object.getOwnPropertyDescriptors(proto));
    return constructor;
};

var Person = Object.extend({
    constructor: function (name) {
        this.name = name;
    }
});

var SomeGuy = Person.extend({
  constructor: function (foo) {
    Person.apply(this, arguments);
  },
  method: function () {
  }
});

var dude = new SomeGuy();

(I changed it to Function.prototype.extend to avoid mentioning classes at all)

Anton Kovalyov
Owner
valueof commented May 07, 2012

@rwldrn What I meant is that I can't write something like class Hello {} and make it work in current/older browsers by including es6shiv.js.

Nicholas C. Zakas
nzakas commented May 07, 2012

@juandopazo Actually "type" is what these things are in JavaScript. Their primitive types and their reference types, that the nomenclature that the spec uses, and that's the nomenclature going forward with things like Struct type. Even the ES6 classes are not truly classes, they are just wrappers around types. Calling something a class when it isn't is what's confusing.

I think that doing nothing is a nice idea, the reason that the class proposals keep coming is because there is a problem. The problem isn't that JavaScript doesn't have classes, it's that it's a huge PITA to make something that acts like classes in JavaScript. There are far too many steps involved in defining your own type, especially if it inherits from something else.

I actually don't care if the syntax is new Type() or Type.create or whatever so long as it solves the problem of custom types being difficult to define in a logical way.

By the way, I'm not against any new syntax, I would just like any new syntax to desugar to something that can be supported in ES5.

And I don't want to call something a "class" when it really isn't.

Sasha Chedygov

If we add special syntax for classes, we might as well create a new language from scratch, or use Dart or something. (Which I'm personally not against, but I thought the whole point of this was to make it shimmable and backwards-compatible. But I guess that's why we're here--to figure out what to do.)

Marek Stępień
marcoos commented May 07, 2012

"No new syntax" is not a means to help make new features shimmable. ES5's Object.create() uses no new syntax at all, but is not fully implementable in plain ES3 anyway, so there already is NO backwards compatibility (backwards-shimmability?) in the current version of the language. It's an already lost cause.

Thus, the Harmony "class" proposal does the job, reduces source code size and improves readability, no need to clutter it with things like "new Type", full of redundant boilerplate.

Rick Waldron

@nzakas and @antonkovalyov why would you need a shim? Browsers aren't the only JS customer. I actually don't care either way about something that uses the word "class" but I won't stand by while the same old tired-ass, object literal syntax abuse "solutions" are offered in the name of progress.

Raynos (Jake Verbaten)
Raynos commented May 07, 2012

This type system can be done by supporting new <Object>

Where the new operator on an object would call new object.constructor and it would just work.

This feature is called object exemplars.

Juan Ignacio Dopazo

@nzakas I really don't want to discuss the meaning of "class" here, much less with someone with your trajectory. I was focusing on:

  • Avoiding confusions with typeof which is already confusing
  • Avoiding yet another global
  • Insisting that a special syntax and shimmable API like Function.prototype.extend can be both standarized to serve the short and the long term.

I would just like any new syntax to desugar to something that can be supported in ES5.

Sure, what CoffeeScript does.

Nicholas C. Zakas
nzakas commented May 07, 2012

@rwldrn Without a shim, you're left with writing two solutions to the same problem. New syntax will always cause an error in browsers that don't support it. That means you can't even think about using new syntax until all of your customers are using browsers that support it or else you end up with two solutions. You have one for everybody that can handle the new stuff, and one for everybody that can handle the old stuff. That's a tremendous waste of time, so I would opt to use the one that works everywhere. That means that the new syntax is essentially useless to me.

The advantage of having shimmable functionality is also for feature detection. How can I test that the browser supports some feature? For ES5, I could do:

if (Object.defineProperty) {
    // do something
}

I want to be able to do the same thing for ES6.

@juandopazo the only confusing part of typeof is that typeof null == "object", other than that, it behaves as expected. This would have no effect on what I'm talking about. The battle for avoiding globals is noble, but it's futile. ES6 already introduces a bunch of globals.

I was modeling my approach after what already exists in ES6. As I said in a previous comment, I'm not religious about the actual API or syntax, yours is just as fine as everything else.

Rick Waldron

@nzakas Again, you're using that dirty word "browser" as a crutch for your argument. The browser is no longer the sole customer here. The very moment anything is introduced in v8, it will be freely available to use in node development.

Also, if you let obsolete browsers determine the future, then everything looks grim. Try thinking about all of these things without any notion of an out of date browser to cloud your thoughts.

Anton Kovalyov
Owner
valueof commented May 07, 2012

@rwldrn Browsers are still the biggest JavaScript customer. Disregarding the browser in favor of Node is not very nice. And future does look pretty grim for web developers—because JS.Next is basically a new language that most web developers will have to use with transpilers. You know what else we can use with transpilers? The language that has better DOM implementation, iterates much faster than JavaScript and will most probably have native support in Chrome.

Sasha Chedygov

@rwldrn Except our web apps still need to work in "obsolete browsers", so that's not an argument you can make.

Rick Waldron

@russiancow @antonkovalyov please don't resort to being dramatic, I'm simply asking you to think outside of the browser - that's all. I don't appreciate having very obvious facts repeated to me as though I was somehow unaware. "Browsers are still the biggest JavaScript customer" no shit? You don't say! "Except our web apps still need to work in "obsolete browsers"" orly? I work on jQuery, I'm painfully aware.

I'm not trying to troll or get aggro here, but I'm definitely frustrated with the narrow-mindedness of the arguments. Step outside of the box please.

Thanks <3

Anton Kovalyov
Owner
valueof commented May 07, 2012

@rwldrn In JavaScript, as I see it, there are things that would be nice to have (like ...) and I don't mind having new syntactic structures for them. But there are also things that are really big pain points—and this is one of them. For such issues I want a fix that can be used as soon as possible. If that's a no go—I will respectfully agree and pick another language.

This is not about being dramatic this is about being practical. Whatever solution fixes my day-to-day problems quicker and better wins.

Nicholas C. Zakas
nzakas commented May 08, 2012

@rwldrn you're making some assumptions about my argument that are incorrect. Yes, I am focusing on the browser use case here, because that's the area where there will be problems with new syntax. As I said, I'm not against adding new syntax, just so long as there's a shimmable solution for older browsers. On the server, where you can determine what version of Node.js you're using an upgrade easily, I definitely don't have as many concerns.

You absolutely must consider older browsers when evolving JavaScript. Even if you suppose the best case scenario of one year to implement ES6, that's still a year where developers will be frustrated because they can't use new language features. Yes, in the hypothetical future where browsers automatically update and we never had users on older browsers, this won't be an issue as much. In the meantime, for those who are making money off of the Internet, you can't simply disregard a large portion of your audience because it's inconvenient for you to write code for them.

In any event, I already stated my position and reasoning several times here and I don't think I'm doing anything but repeating myself at this point. Therefore, I'll stand on my previous comments.

Rick Waldron

@nzakas Of course, ultimately I completely agree and we see eye-to-eye across the board, I'm just trying to force myself and subsequently others, to think outside the realm of limitations.

Sebastian Porto
sporto commented May 10, 2012

Totally agree with this. Classes are not necessary and may lead to more confusion.

I quite like the syntax where the 'new' keyword is not used.

boo.js:

var Cat = Animal.derive({
    ...
})

var cat = Cat.make();

Ember.js:

var Cat = Animal.extend({
    ...
});

var cat = Cat.create();
Raynos (Jake Verbaten)
Raynos commented May 10, 2012
var Cat = Animal <| {
    ...
}

var cat = new Cat

Object exemplars allow for this syntax.

Note that Cat is an object and new <Object> syntax is similar to the make or create method.

James M. Greene

My biggest concern with ES6 is definitely the same as many others mentioned here: it's going to come out and be supported in Node.js and all the great new browsers! Awesome... but, if it is not shim-able to older browsers, it either adds work for me to ensure our static content pushes are transpiled (but only used for certain browsers because I would definitely want to take advantage of the true implementation in the supported browsers, right?).

Furthermore, what I find lacking in all of these examples is the ability to have private members/methods at all. That's the trouble with object literals! We must use a Function shim in order to accommodate privates.... :disappointed:

Rick Waldron

Is anyone aware that that EcmaScript 1 didn't have RegExp (the literal form IS SYNTAX), nor try/catch, which is also... SYNTAX.

Rick Waldron

I almost forgot... FunctionExpression too!

Juan Ignacio Dopazo

Fun and snarky comments aside...

@JamesMGreene My biggest concern with ES6 is definitely the same as many others mentioned here: it's going to come out and (...) adds work for me to ensure our static content pushes are transpiled

No, it won't. ES6 will affect you in two ways:

  • There will be new APIs available. Some will be shimmable, some won't. You'll get to use some and you'll have to wait for others. That's the same situation we have now for lots of browser features from localStorage to hardware access in mobile.
  • There will be an optional mode for scripts which will run with the new syntax and the backwards incompatible changes like typeof null == "null". This scripts will be in constant strict mode and won't run in older browsers obviously. But it'll be there for when old browsers die and for other EcmaScript environments. So that won't add more work for you either.

In practice, I think there will be new syntax available in the normal/old mode. You can try let and const in some browsers now. But there won't be any backwards incompatible changes affecting the entire web. That would kill the web. So where's your extra work? ES.next is for the future, not for next week. Unless you're writing a weekend project and don't mind the consequences.

So, like @rwldrn says, new syntax needs to be there to evolve the language, looking at the future. Does that mean there shouldn't be new APIs that help solve the same problems earlier? Clearly no.

Kit Cambridge
Owner

So...it seems as though we have reached an impasse.

As much as I detest classes, I'm beginning to think that there is a place for them in Harmony. Classes have been consistently requested by a vocal majority of developers, especially those accustomed to other languages. Even Ruby—which is perhaps the most dynamic language that I've used—has classes, though its notion of inheritance is actually much closer to JavaScript's than that of other classical languages.

I've never found JavaScript's prototypal inheritance particularly difficult to comprehend; in fact, I'd argue that it's more flexible and elegant than the classical paradigm. But developers from other languages—or those wishing to cut down on boilerplate—aren't necessarily interested in elegance (which itself is subjective); they just want to get things done. Unfortunately, a different inheritance model detracts from that objective. The fact that almost every JS library in existence has some kind of class or inheritance helper attests to that...and this myriad of differing implementations doesn't make things any easier.

With that in mind, I think the "maximally minimal classes" proposal (thanks for the link, @rwldrn) is an excellent compromise. It formalizes the common patterns seen in current inheritance helpers, while retaining much of the language's dynamic nature. Alas, it doesn't address the concern of single-root inheritance (Ruby provides the include method, so this issue isn't as applicable).

Let's also keep in mind that classes won't replace constructor functions—they'll just make it easier for developers familiar with the classical pattern to acclimate to the language. And, for experienced developers, to better communicate their intent.

Rick Waldron

@kitcambridge brilliant summary :)

Axel Rauschmayer

I’ve fought long and hard for object exemplars (prototypes as classes) which would finish what Object.create() has started and would be simpler than constructors. They might still be supported in some capacity, but seem to be too different from current code to become adopted as the object factory mechanism. Class declarations, as described by the maximally minimal classes proposal, are the second-best solution. Their main benefit is that they turn current best practices into a syntactic construct and make subtyping easy. After class declarations, we’ll hopefully see much less inheritance libraries.

I don’t agree with the opinion that JavaScript needs classes – it doesn’t need to become like other class-based programming languages. Class declarations may be a good compromise, because they look like classes and are even named like them. But they are just syntactic sugar for constructors.

More of my thoughts on class declarations and object exemplars are here: http://www.2ality.com/2011/11/javascript-classes.html

angus croll
Owner
angus-c commented May 13, 2012

So yesterday evening I had something of an epiphany (solidified after discussions with @kitcambridge).

It started when I read this comparison of classical vs. prototypal inheritance
http://carnotaurus.philipcarney.com/post/3010984357/classes-versus-prototypes-some-philosophical-and

"In general, when working with prototypes, one typically chooses not to categorize but to exploit alikeness"

By this measure, JavaScript is not great at prototypical inheritance, in fact even though its prototypes are dynamic instances, its prototype mechanism has more in common with classical OOP:

1) prototype chains are single rooted, each object can only inherit from one prototype

2) the new <constructor> syntax encourages us to define prototypes as formal types (classes) instead of arbitrary properties. Object.create was a big improvement in this regard (Crockford was definitely on to something here) since it allows object to inherit from anonymous and ad hoc literals rather than formal types, but still the single inheritance model prevents us from exploiting prototypes to their fullest. Also, no-one uses Object.create.

3) Mixins, in which an object assumes properties from multiple objects (and not types) is possibly truer to the prototypal ideal, it removes the need for categorization and focuses on borrowing behaviours (the 'does' not the 'is')

Bottom line, if you don't like classical hierarchies then you probably won't like JavaScript's prototype chains either, since they have much in common (pre-meditated categorization, formal types, a single root).

And so 'class' is probably quite an appropriate name for JavaScript prototypes (or their constructors). And class syntax might not make much difference, other than making prototype chains a little easier to create and therefore discouraging use of the more genuinely prototypal mixin patterns.

Dave Herman
dherman commented May 13, 2012

@angus-c I agree with most of what you're saying. I do want to point out that the proposed ES6 classes are not antithetical to mixins/traits, they just don't provide much syntactic convenience for it yet. For starters, you can use dynamic mixin patterns for constructing the parent:

class Foo extends mix(A, B, C) {
    // ...
}

Ideally, it'd be cool to grow the class syntax eventually to support built-in syntax for trait composition, for all the reasons you state: single inheritance is weak and brittle, and composition is much more flexible and powerful. You might imagine it looking something like:

class Foo extends Bar with A, B, C {
    // ...
}

But I'm not sure what the exact semantics of trait composition should be. (For example, we're not going to grow the object model to support multiple prototypes — that would be far too radical.) It'll take time and work to think through (i.e., beyond the ES6 time frame). There was one proposal for traits by Tom Van Cutsem and Mark Miller that got tabled a while back. I don't remember all the details but ISTR they made some design decisions that seemed wrong for JS. Anyway, the current thinking of most of the committee has been that, with classes, we should build these pieces up little by little and not over-reach for ES6. This is what we mean by "minimal" classes. It's a building block that we could grow down the road.

Dave

angus croll
Owner
angus-c commented May 13, 2012

Ideally I'd love to see an alternative to extends (which to me will always have connotations with single inheritance). I suppose something like

class Foo mixes A, B, C

Except, and I think this might be my real issue, I don't want to have to define a new type in order to have mixins/traits happen. And this is where prototypes might be more expressive because I can either mix into a type or an instance with the same syntax

Foo.prototype.mix(A, B, C);
myInstance.mix(A, B, C);     
Dave Herman
dherman commented May 13, 2012

I was assuming the extends really would be the single-inheritance part. Like I say, the semantics isn't clear.

I don't want to have to define a new type in order to have mixins/traits happen. And this is where prototypes might be more expressive because I can either mix into a type or an instance with the same syntax

Yes, that's mutation. Mutation is a mixed bag: it's more expressive but also dangerous in that it can break modularity. Same story as monkey-patching: it's powerful, because you can add functionality post-hoc, but it's more fragile because it relies on everyone cooperating not to step on each other's toes.

That ability will never go away, since we'll always have prototypes. The question is whether we want new syntax to encourage mutating inheritance/component structure. One approach that tried to allow this kind of post-hoc extension without having non-local effects was the "scoped object extensions" proposal. There were technical problems with that specific proposal that I think made it unworkable, but it was inspired by the good idea of allowing a local portion of code to extend an object's functionality without those extensions affecting other portions of code. That's got the flexibility of mutation without the unintended consequences.

Dave

angus croll
Owner
angus-c commented May 13, 2012

I guess "scoped object extensions" is more about sandboxing, and I'm not wild about it because you end up with two-tier object definitions and have to know whether your module has the enhanced version or not.

I was thinking more in terms of using mixins to augment ad hoc, user-defined instances at the point of their creation as opposed to modifying post-hoc, an existing instance or prototype of a pre-defined type. The following example is probably not useful outside of unit tests, but illustrates the pattern without needing much code:

var testCircle = ({radius: 4}).mixin(withCircle);
testCircle.area();

A more useful example might be a storage utility which is augmented based on available storage type

function createStorageAgent() {
  var storageAgent = {//generic methods};
  storageAgent.mixin(window.localStorage ? withLocalStorage : withUserData);     
  return storageAgent;
}

storageAgent = storageAgent || createStorageAgent();
storageAgent.push({a:44});

And of course you can mix in to the prototype if you want to augment the entire type

UserAction.prototype.mixin(withLogging);

In other words its just a shorthand for something you can already do with property assignment, but allows similar properties to be grouped into objects or functions - mixins all the way. (that particular syntax might be a no-no because I guess we're not adding anything else to object prototype)

Dave Herman
dherman commented May 13, 2012

Re: scoped object extensions: nothing to do with sandboxing, no; it's about augmenting types with additional functionality -- methods, primarily -- in a local scope. But I see that it's not the use case you're talking about. (To be clear, it's also not something I'm advocating, at least not as it was proposed. The proposal had serious technical issues.)

Re: your example:

var testCircle = ({radius: 4}).mixin(withCircle);

Since you're only talking about augmenting new objects, it doesn't need mutation. It would be enough to have mixin/trait composition that creates an object from scratch by combining properties from multiple sources. And this way there's no risk of accidentally modifying an object you don't own. But as long as the types we're talking about here are dynamically constructed, you can make all the dynamic decisions you want to construct a new type by conditionally mixing in different components.

IOW, it's about whether you do the mixing before you construct the new object or after you construct the new object. I claim the latter is more power than you need and an invitation to bugs and misuse.

Re: "I shouldn't have to define a new type", it doesn't have to be heavyweight. It doesn't have to involve a declaration, for example. I doubt the exact syntax of your testCircle example is likely to happen, because we can't really add new methods to Object.prototype. But you could imagine something like:

var testCircle = mixin(circle, { radius: 4 }).create()

or maybe

var testCircle = new (class with circle, { radius: 4 })()

or something like that.

Dave

angus croll
Owner
angus-c commented May 13, 2012

(fair enough, I looked at this http://wiki.ecmascript.org/doku.php?id=strawman:scoped_object_extensions and it reminded me, in goal not means, of http://msdn.microsoft.com/en-us/magazine/gg278167.aspx. But yeah either way I share your concerns)

Agree that mixin before (or concurrent with) creation minimizes surprises.

Peter van der Zee
qfox commented May 13, 2012

In most of my current code I'd just like some sugar for the

function Foo(){}
Foo.prototype = {
  prop: 5,
  method: function(){},
};

pattern. I actually don't really need to extend that often, but I do think it would be nice to be able to hook up your new class to an explicit existing prototype chain, other than that of Object. From the top of my head, I don't really know what else I'd need for class syntax. (I really don't give a damn about encapsulation...) I think the mixins examples are (more primitive?) workarounds for such inheritance paths. Want to inherit from multiple sources? Put them in the prototype chain.

angus croll
Owner
angus-c commented May 13, 2012

@qfox:

Because JS prototoype chains are linear we are locked into certain re-use patterns

so A wants behavior from B and C?
ok A extends B, B extends C.

Now D only wants behavior from B, not from C.
But, if D extends B it gets C by default.

Mixins can cherry pick what they want because they are only concerned with behavior, they don't define relationships between types.

A with (B, C);
D with (B)

Rick Waldron

@qfox that would become something like this...

class Foo {
  constructor() {
  }
  method() {
  }
}

I'm actually not sure how your property prop would be defined, let's see if @dherman can clarify this one.

Raynos (Jake Verbaten)
Raynos commented May 13, 2012

note that one can have mixins with plain js easily.

It's just an extend function

Foo.prototype.mix(A, B, C);
myInstance.mix(A, B, C); 

becomes

extend(Foo.prototype, A, B, C);
extend(myInstance, A, B, C); 

Mixins and types seem to be orthogonal.

Kit Cambridge
Owner

@Raynos Not quite...Ruby:

module A
  def first
    puts "A: First method."
  end

  def second
    puts "A: Second method."
  end
end

module B
  def second
    puts "B: Second method."
  end

  def third
    puts "B: Third method."
  end
end

instance = Class.new { include A; include B }.new

instance.first
# => A: First method.

instance.second
# => B: Second method.

instance.third
# => B: Third method.

module A
  def first
    puts "A: Overridden method."
  end
end

instance.first
# => A: Overridden method.

This isn't currently possible in JS because an object can only inherit from a single parent.

Dave Herman
dherman commented May 13, 2012

@rwldrn: The minimal classes don't provide declarative syntax for it, so you'd have to add it to the prototype after the class declaration:

Foo.prototype.prop = 5;

Which is a bit of a bummer, but maximally minimal classes tabled all properties other than prototype methods. So computed prototype properties or class properties were deferred for another day, in the interest of creating a core foundation that we could build on.

Also, apparently including constructor properties in a proposal means I get to endure accusations that I hate JavaScript. :(

Dave

Rick Waldron

Honestly, I can't remember when the last time I actually put a non-method property on a prototype - doing so never seems right.

angus croll
Owner
angus-c commented May 13, 2012

@dherman its a stretch to claim I said you hated JavaScript. And I know you don't. But apologies anyway,and I regret offending you. I'm glad your max-min proposal won the day over the more horrific class proposals that were considered.

Dave Herman
dherman commented May 13, 2012

@angus-c Sorry, I should probably keep it off thread.

@rwldrn ISTR finding a use for an inherited data property some time recently and thought "hey that actually seems valid." But now I can't remember what it was. Anyway, I agree it's a rare case, and didn't lose any sleep over tabling it.

Dave

Peter van der Zee
qfox commented May 14, 2012

Honestly, I can't remember when the last time I actually put a non-method property on a prototype - doing so never seems right.

I always want instance properties on the prototype object, even if they aren't shared or some initial value. In a group project (or even for yourself in six months) you'll appreciate being able to instantly see what instance properties an object might have, without having to search for this everywhere (or worse, if those are accessed dynamically). I advocate doing so anywhere I go, it's just part of clean coding in my oppinion.

In most cases, those instance properties are initialized to null (most of the time get a real value in the constructor) but sometimes it's just handy, like setting to 0 for counters.

I didn't know it wasn't possible to add them in the current proposal and find that a huge huge disappointment :(

Juan Ignacio Dopazo

The only example I've seen in the wild that makes any sense is that YUI uses data properties in the prototype for storing templates for widgets. This allows for overwriting those templates when subclassing the Widget.

Misha Reyzlin
gryzzly commented May 22, 2012

I like that @nzakas’s solution is easily shim-able. However, Backbone's self-propagating extend and its alikes (for example @gozala's http://jeditoolkit.com/2011/04/23/Yet-another-take-on-inheritance.html#post) seems like a better approach to me, while still being shim-able.

(Soreλ\a)

The main problem with having a class proposal, in the first place, is still the divergence in the mental models between classical inheritance and prototypical delegation — yes, a single delegation slot is not as powerful as, say, Self's multiple delegation, but the basic concepts are still the same.

As such, everytime I see a:

class Foo extends Bar {
  /* lotsa stuff */
}

I'm led to believe I'm working in a classical object oriented language, when that's clearly not the case. In short, I'd consider it to be an extremely poor UI — which is all syntax in a programming language really is.

Object Exemplars, on the other hand, don't carry all the weight associated with classes, and "get the job done", or at least for the majority of use cases, as far as I know. So, I don't need a class proposal is really needed, given there are other ways of achieving the same thing, with arguably the same conciseness and level of abstraction.

var Foo = Bar <| { /* lotsa stuff */ }
Rick Waldron
var Foo = Bar <| { /* lotsa stuff */ }

This was killed at the last TC39 meeting - it's time for everyone to move on.

Dynamic classes aren't a new thing.

victornpb

I've always used this pattern, is very readable and easy to maintain.
I'm 100% happy with this way of doing OOP


function Person(lastName, name) { //ClassLike constructor

    this.firstName = name | “Unammed”; 
    this.lastName = lastName | “Unnamed”;
    this.age = 0;

     var weight = 150; //pounds
     var health = 100;
     var energy = 2500; //cal


   this.introduce = function () { 
      console.log("I'm " +self.firstName + ' ' + self.lastName); 
    }

    this.walk = function(miles){
        energy -= miles * 40;
    }
    this.eat = function(calories){
        energy += calories;
    }
    this.sleep = function(hours){
        energy -= 0.42 * weight * hours; 
    }
}

var food = {
    apple: 80,
    bannana: 101,
    cheeseburger: 570
}

var bob = new Person('Duncan','Bob'); //create new instance and init
bob.introduce(); //I'm Bob Duncan
bob.eat(food.cheeseburger);
bob.sleep(8);

-1

Rick Waldron

class Person() {
  private @weight, @health, @energy;
  constructor(lastName = "unnamed", name = "unnamed") {

    this.firstName = name; 
    this.lastName = lastName;
    this.age = 0;

    this.@weight = 150; //pounds
    this.@health = 100;
    this.@energy = 2500; //cal
  }
  introduce() { 
      console.log("I'm " +this.firstName + ' ' + this.lastName); 
   }
  walk(miles) {
       this.@energy -= miles * 40;
   }
   eat(calories) {
       this.@energy += calories;
   }
   sleep(hours) {
       this.@energy -= 0.42 * this.@weight * hours; 
   }
}

var food = {
    apple: 80,
    bannana: 101,
    cheeseburger: 570
}

var bob = new Person('Duncan','Bob'); //create new instance and init
bob.introduce(); //I'm Bob Duncan
bob.eat(food.cheeseburger);
bob.sleep(8);

...the choice of a new generation.

Rick Waldron

...granted, I changed those own instance methods to proto methods; probably better for program memory usage anyway.

victornpb

@rwldrn I personally don't think how this is better, you have to explicitly declare all private variables, when closure rules are IMHO much better. And if the @ prefix is mandatory it's just painful it's like a second wasted in life every time you have to shift-2 for every variable, also it would considerable increase the source size.
I'm curious about if proto implementation is in fact more efficient, do you have some evidence or source?

Rick Waldron

Of course it's more efficient, the prototype is memory allocated once, versus own instance methods which are allocated for every instance, everytime a new instance I created. This is not subjective, it's science.

I hear you on the @-names, they are just one of several possible futures, and your feedback is appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.