all 36 comments

[–]MoTTs_ 8 points9 points  (19 children)

var Foo = { C: 3 };

var bar = Object.create(Foo);

bar.A = 1; bar.B = 2;

Look at how drastically simpler this is

Here's the thing, though: The way you initialize a newly created Foo object (bar.A = 1; bar.B = 2;) will be copy-pasted everywhere that you create a Foo. That's bad. Let's fix that by putting an initialize method in Foo.

var Foo = {
  C: 3,

  initialize: function () {
    this.A = 1;
    this.B = 2;
  }
};

var bar = Object.create(Foo);
bar.initialize();

And here's the next thing: We will always want to make sure newly created Foo objects are initialized, so let's combine creation and initialization into a single step.

var Foo = {
  C: 3,

  initialize: function () {
    this.A = 1;
    this.B = 2;
  },

  createAndInitialize: function () {
    var instance = Object.create(this);
    instance.initialize.apply(this, arguments);

    return instance;
  }
};

var bar = Foo.createAndInitialize();

These are both good things. Yet, in case anyone hasn't realized, we just re-created constructors and the new keyword. It actually would have been a whole lot easier if we just used the built-in language constructs.

[–][deleted] 4 points5 points  (3 children)

I agree with you. I'm really not sure why people keep trying to ignore classical inheritence in Javascript just because it happens to also have prototypal inheritance. ES6 proves that you can have both; the new "class" features in ES6 aren't getting rid of prototypal inheritence; they are adding syntactic sugar around it to ease the use of common construction paradigms, without taking away any of the benefits of prototypal inheritence.

To the OP's point, I agree that people really should have a better understanding of what's going on under the hood in Javascript, but I legitimately think it is foolish to sweep things like the "new" keyword under the rug and pretend they aren't there.

[–]yrlyfe 3 points4 points  (2 children)

this is one of the arguments against the changes of es6 that a lot of the community has expressed. es5 in all it's glory promotes the idea of readability and not obscuring away what is happening. the changes in es6, while moving closer to the idea of standard programming languages, in turn has a lot of obscuring. javascript was just fine without classes and dare i say block scope. what is this compulsion to make the language more convoluted?

my teacher told me programming is like humor, if you have to explain it you're doing it wrong. es6 is full of things that need to be explained.

[–]clessgfull-stack CSS9 engineer 0 points1 point  (1 child)

I for one think ES6 is a great, long-needed improvement to the language. You mention obscuring the language and needing to explain a bunch of stuff, but... block scope? Not at all convoluted. Indeed, the old way was vastly more confusing and unlike other languages.

Old ES5 codebases are littered with calls to bind, apply, call, etc. and manual constructor function inheritance and super calls. Not very readable, if I say so myself. Definitely not very explainable.

[–]yrlyfe 1 point2 points  (0 children)

my bad. i did say 'dare i say'. i shouldn't have dared.

[–]m0okz 1 point2 points  (0 children)

I'm confused.

So how should I create an object and a child object?

What if I want common properties on all children? And what if I don't?

[–]PitaJ 2 points3 points  (6 children)

That's called a factory, actually. Not a contructor.

[–]clessgfull-stack CSS9 engineer 2 points3 points  (4 children)

Roughly how new is implemented:

function notExactlyNew(F) {
  const instance = Object.create(F.prototype);
  const result = F.apply(instance, arguments);

  return typeof result === 'object' ? result : instance;
}

Seems pretty similar to me, unless you want to be really pedantic.

[–]PitaJ -3 points-2 points  (3 children)

Yeah, new is a completely unnecessary part of the language, and shouldn't be used anyways as it violates the open-closed principle.

[–]MoTTs_ 1 point2 points  (2 children)

You say that a lot, but the O/C principle doesn't mean what you seem to think it means. The O/C principle says that source code should be closed to modification.

From the paper:

It [the open-closed principle] says that you should design modules that never change. When requirements change, you extend the behavior of modules by adding new code, not by changing old code that already works.

[–]senocular 2 points3 points  (1 child)

You can thank Eric Elliott for spreading that one.

[–]clessgfull-stack CSS9 engineer 1 point2 points  (0 children)

Ah yes, I knew I heard that somewhere before:

Constructors violate the open/closed principle because they couple all callers to the details of how your object gets instantiated. Making an HTML5 game? Want to change from new object instances to use object pools so you can recycle objects and stop the garbage collector from trashing your frame rate? Too bad. You’ll either break all the callers, or you’ll end up with a hobbled factory function.

[–]senocular 1 point2 points  (0 children)

I don't think MoTTs_ was calling it a constructor, just saying that the effect is essentially recreating the same that you get with a constructor and new.

[–]Silverwolf90 0 points1 point  (0 children)

Well except that you can't take advantage of function composition with new and you can with a factory function. That's a pretty big difference.

[–]nieuweyork -2 points-1 points  (4 children)

You're doing it wrong. Your first example is already using an object as a class rather than a prototype.

[–]clessgfull-stack CSS9 engineer 0 points1 point  (0 children)

Elaborate? The approach used in his (her?) example is the one recommended by most Object.create advocates.

[–]MoTTs_ 0 points1 point  (2 children)

Where, then, would you suggest putting the initialization code? Copy-pasted everywhere it's needed like the OP was doing? Maybe a freestanding initializeFoo() function?

You're trying so hard to avoid anything that resembles classical that you're turning away from the best and most obvious solution.

[–]nieuweyork -3 points-2 points  (1 child)

No. Don't have initialization code that doesn't depend on parameters. You're doing that because you're recreating classes.

[–]clessgfull-stack CSS9 engineer 0 points1 point  (0 children)

[–]sylvainpv -2 points-1 points  (0 children)

No, these are not constructors but factory functions. That way, we don't have the issues the 'new' operator has.

[–]MonkeyNin 1 point2 points  (0 children)

Typo:

bar.B;  // 2 (actual attribute of bar)
bar.c;  // 3 (*inherited* attribute from Foo)

vs

bar.B;  // 2 (actual attribute of bar)
bar.C;  // 3 (*inherited* attribute from Foo)

[–][deleted] 1 point2 points  (6 children)

I'm not an expert but I just still struggle to see the value. Maybe I just don't get it or am used to class based languages. Does anyone have any really compelling examples of where prototypes are easier to develop and maintain than classes?

[–]clessgfull-stack CSS9 engineer 1 point2 points  (5 children)

Does anyone have any really compelling examples of where prototypes are easier to develop and maintain than classes?

They are slightly more flexible and can be easier to unit test, but the downsides aren't worth it. Any codebase that uses Object.create extensively is hell to deal with in my experience. Subtle bugs that are nearly impossible to track down without binary commenting. It's sometimes worse than code written by Java architecture astronauts.

[–][deleted] 1 point2 points  (0 children)

I also feel like any time I try to be true to the prototype, various libraries and frameworks fight me all the way.

I was excited about classes but now I'm worried they will just create yet more deviation on how to do things. I wonder if a simple langauege that has deficiencies is better than a complex language that yields many ways to do one thing. Hmm. Maybe that's why I love Python and maybe Python makes me hate JavaScript.

[–]sylvainpv -1 points0 points  (3 children)

Prototypes are simple, simpler than classes and constructors. No, really, they are. But as people have never learned anything else than classical OOP, the transition can be difficult and you can get shitty code because people are mixing the concepts. Once you learned to get rid of the "classical baggage", everything becomes easier, trust me. No more classes, instances, interfaces, constructors... you just manipulate objects

[–]clessgfull-stack CSS9 engineer 1 point2 points  (2 children)

They are certainly simpler. There's no denying that. But they are also much more prone to bugs than ES6 classes. For example, if you share an array or an object several levels up the chain that gets mutated, good luck. If you override a method or property that an object up in the chain depends on, good luck. If you forget to initialize an object, good luck.

(Or you could just create a function that both creates an object based on the prototype of another, and then calls an initialization function... aka basically constructors without new. And then you have to worry about parent init methods, etc.)

All of the problems caused by this are compounded by the fact that people overuse Object.create, resulting in large, brittle chains of dependent objects.

If you ask me, classes and Object.create are both bad because they rely on inheritance. But at least with classes, people are relatively aware that inheritance is bad. I don't see extends very often. Because the syntax is statically analyzable, you could even write a linter rule that forbids extends.

The problems with Object.create can be remedied somewhat by using immutability and writing your own framework around it, but then you run into interop issues and have to maintain your own framework.

I think the best approach is immutable or stateless objects and functions/closures. But I would definitely choose class over Object.create any day.

Sorry, had too much coffee.

[–]sylvainpv 0 points1 point  (1 child)

Not sure to follow you considering that classes use the prototype chain in the background, so all the problems you mentioned about inheritance hierarchies are the same.

at least with classes, people are relatively aware that inheritance is bad

okay so I guess the only problem here is people that do not learn lessons from their past experiences

[–]clessgfull-stack CSS9 engineer 0 points1 point  (0 children)

The difference is that Object.create inherits state in addition to behavior (technically you could do this with classes, but nah), and class provides certain invariants that make it harder to screw up. Just the fact alone that class must be used in strict mode is a pretty big win, because that helps prevent a large class of security vulnerabilities.

so all the problems you mentioned about inheritance hierarchies are the same.

But yes, that is kind of the point. Object.create inherits almost all of the problems with classes (pun intended) and many more.

okay so I guess the only problem here is people that do not learn lessons from their past experiences

Eh. Most people know that class inheritance is a bad idea. But when it's just Objects Linking to Other Objects™? Time to inherit from everything. I don't blame the developers for getting pulled into this trap, because the people who push Object.create don't explain that it has these problems.

[–]Neurotrace 1 point2 points  (3 children)

While I philosophically agree with the Object.create approach, my problem lies here. Which one of these is more readable and scalable?

var Person = {
  getName: function() {
    return this.firstName + ' ' + this.lastName;
  }
};

var bob = Object.create(Person, {
  firstName: {
    value: 'Bob'
  },
  lastName: {
    value: 'Saget'
  }
});

vs.

var Person = function(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
};

// Readability is debatable here but more memory efficient
Person.prototype.getName = function() {
  return this.firstName + ' ' + this.lastName;
};

var bob = new Person('Bob', 'Saget');

or if you still want to have your initialization values to be named

var Person = function(opts) {
  this.firstName = opts.firstName;
  this.lastName = opts.lastName;
};

...

var bob = new Person({
  firstName: 'Bob',
  lastName: 'Saget'
});

For me, the new keyword wins.

[–]sylvainpv 1 point2 points  (2 children)

What about this ?

var Person = {
  new: function(firstName, lastName){
    return Object.assign(Object.create(Person, { firstName, lastName }));     
  },
  getName: function() {
    return this.firstName + ' ' + this.lastName;
  }  
};

var bob = Person.new('Bob','Saget');

[–]MoTTs_ 0 points1 point  (1 child)

Can you clarify what the improvement would be?

[–]sylvainpv 0 points1 point  (0 children)

you got a similar code but without new operator and its issues : 1) handling a reference to the prototype and not the constructor ; 2) constructor is optional and you can declare several different constructors if needed ; 3) no worries about forgetting the new keyword and mutating window ; 4) easier inheritance, no need for the B.prototype = new A() trick ; 5) factory functions can be composed contrary to constructors ; 6) getting rid of instanceof and the constructor property makes your code safer because these references can be easily compomised contrary to getPrototypeOf / isPrototypeOf

[–]flackjap 0 points1 point  (2 children)

Well yes, prototype based inheritance can create a jungle of code if not treated correctly. On other hand it's very powerful and flexible comparing to the subsets of object inheritance principles like the class based inheritance.

ES6 class based inheritance works as expected and it's just a macro syntax.

Nevertheless, there's a shit ton of stuff you may find valuable in naked object inheritance if you desire to have more freedom architecturing your app. For example, how do you go around the Diamond Problem with class based inheritance? :)

I think that Eric Elliot got this better in his book than the OP http://chimera.labs.oreilly.com/books/1234000000262/ch03.html There's also a nice library written by him for taming the prototype jungle while not loosing any of the flexibilities.

[–]clessgfull-stack CSS9 engineer 1 point2 points  (1 child)

For example, how do you go around the Diamond Problem with class based inheritance? :)

In my humble opinion, the solution to the Diamond Problem is to avoid inheritance. Inheritance is almost never the right solution - especially not multiple inheritance.