12

Reading through the following question, I feel the majority of the answers miss the point of why some people (Crockford) choose not to use the "new" keyword. It's not to prevent accidental calling of a function without the "new" keyword.

According to the following article by Crockford regarding prototypal inheritance, he implements an object creation technique that more clearly demonstrates the prototypal nature of JS. This technique is now even implemented in JS 1.8.5.

His argument against using new can be more clearly summed up as:

"This indirection was intended to make the language seem more familiar to classically trained programmers, but failed to do that, as we can see from the very low opinion Java programmers have of JavaScript. JavaScript's constructor pattern did not appeal to the classical crowd. It also obscured JavaScript's true prototypal nature. As a result, there are very few programmers who know how to use the language effectively."

I don't necessarily consider "new" harmful, but I do agree that it does "obscure JavaScript's true prototypal nature," and therefore I do have to agree with Crockford on this point.

What is your opinion of using a "clearer" prototypal object creation technique, over using the "new" keyword?

Community
  • 1
  • 1
Steve
  • 53,375
  • 33
  • 96
  • 141

3 Answers3

13

You right, using new is considered harmful as it doesn't place enough emphasis on OO in JavaScript being prototypical. The main issue is that acts too much like classical classes and one should not think about class. One should think about Object's and creating new Objects with existing objects as blueprints.

new is a remnant of the days where JavaScript accepted a Java like syntax for gaining "popularity".

These days we do Prototypical OO with Object.create and use the ES5 shim.

There is no need for new anymore.

Before the days of ES5 being commonly implemented (only FF3.6 and IE8 are slacking) we used new because we had little choice. someFunction.prototype was the way to do prototypical inheritance.

I recently wrote up a "nice" demonstration of Object.create although I still need to iron out some kinks in the usage. The important thing is to separate Objects from factory functions.

Community
  • 1
  • 1
Raynos
  • 166,823
  • 56
  • 351
  • 396
  • *"...only Opera and IE8 are slacking..."* Still a lot of Firefox 3.6 users out there... – T.J. Crowder Jun 16 '11 at 16:53
  • @T.J.Crowder they should upgrade. – Raynos Jun 16 '11 at 17:30
  • @Raynos Does `Object.create` let you override methods with its second parameter? How do you add or override methods with objects made with `Object.create`? See: http://stackoverflow.com/questions/9022519/override-methods-with-prototypal-inheritance – ryanve Jan 26 '12 at 19:07
  • Strange nobody notices the performance drawbacks of `Object.create`: http://jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/27 – Misha Reyzlin Mar 01 '12 at 13:20
  • @gryzzly those are neglible. Browsers optimise what end-users use. The majority of OO is done with `new` so of course it's faster. Sneak `Object.create` heavily into a popular benchmark and Object.create will become fast. – Raynos Mar 01 '12 at 13:50
  • Practicality is important in big JavaScript applications — esp. on these that support browsers that will not be updated. `new` is 40 times more fast in Chrome(!) and that is one very well optimized browser. Anyway, I don't see anything wrong with `new` keyword and calling JavaScript custom types “classes” – “class is a construct that is used as a blueprint to create instances of itself” (from Wiki) – JS constructor does exactly that – describes the instance that will be created when invoking it with `new`. Once you know how to use it, I don't see what's wrong with it. And Crockford's no god :) – Misha Reyzlin Mar 01 '12 at 14:16
  • `new` itself isn't bad. It's constructor functions that are ugly as hell. And besides your object construction shouldn't be a bottleneck in your application, is it then create an optimised `create` function – Raynos Mar 01 '12 at 14:20
10

I don't find that new obscures the prototypical nature of the language for me. Granted I've now spent some years getting to know the language well (having made the mistake originally of diving in and starting writing code without a clue how the language actually worked, to my initial cost).

I find new expressive in a way that prefixing "new" on function names or using a helper function, etc., isn't:

var fido      = new Dog();          // Simple, clear
var rover     = Dog();              // Very unclear (fortunately people mostly don't do this, but I've seen it)
var scruffles = newDog();           // Clearer, but hacky
var fifi      = Object.create(Dog); // (Crockford) Verbose, awkward, not esp. clear

Whether it's prototypical or class-based (and most of that doesn't relate to the new keyword at all), I still think best in terms of functionality grouped together into building blocks (objects or classes or whatever) and then specific objects I can muck about without affecting those building blocks. For me, new as a means of clearly identifying my intent is neither classy or prototypical, it's just clear.

For me, new is just as vital a part of JavaScript as prototypes themselves are, as the wonderfully dexterous functions are. The is is no conflict.

And speaking practically, there are a lot more programmers out there used to using new to create new instances of things than not, and it's a widespread practice in the JavaScript community despite Crockford's efforts (don't misunderstand, I have a lot of respect for Crockford). If I'm staffing up a project, I don't want the retraining hassles.

This is not to say that the way that you define hierarchies of prototypical objects in JavaScript is a thing of beauty and a joy forever. It's a royal pain, especially without the ECMAScript5 enhancements. But define a helper function to help you wire up your hierarchies (whatever you want to call them), and you're done. (See update below.)

[And yes, the whole thing about constructor functions being called without new really is a red herring (you did say you didn't think it was a main point either). You almost never see it happen in the real world. Actually, some of what JavaScript does with that is quite nice (the way String and Number cast, for instance); other parts (what Date does **shudder**) are not so nice...]


As of ES2015 (ES6), creating hierarchies of prototypes and constructors is a breeze, thanks to the new class syntax. No need for helpers anymore.

class Base {
    constructor(name) {
        this.name = name;
    }
    greeting() {
        return "Hi, I'm " + this.name;
    }
}

class Derived extends Base {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
    greeting() {
        return super.greeting() + " and I'm " + this.age + " years old";
    }
}

That doesn't mean we want them for everything, of course. I use Object.create to great effect when I need to extend a single object.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    That is a big style issue with `Object.create`, the lack of syntactic sugar. I seem to create blueprint objects to clone and then a separate factory function for each object having essentially two objects. I think this is more a problem with not understanding prototypical OO and would be solved by spending a few days writing in Self. – Raynos Jun 16 '11 at 16:37
  • 1
    @Raynos: Perhaps. You could always do this: `var nu = Object.create;` and then `var spot = nu(Dog);`. You know, if you want to *really* make things non-standard. ;-) – T.J. Crowder Jun 16 '11 at 16:39
  • I agree that Object.create is not particularly clear. I prefer the "beget" approach that Crockford mentions, but that would involve manipulating Object.prototype. – Steve Jun 16 '11 at 16:40
  • That's not the issue. [Demonstration](http://stackoverflow.com/questions/6337879/oop-programming-in-javascript-with-node-js/6339819#6339819). I duplicate `SomeObject` and `createSomeObject`. @Steve it's mainly about the verbosity of passing in the second parameter for `object.defineProperties` – Raynos Jun 16 '11 at 16:41
  • 4
    @T.J. Haha...nu is the new "new" – Steve Jun 16 '11 at 16:41
  • @T.J.Crowder `var nu = Object.create.bind(Object)` Who knows what will break if the context is `window` rather then `Object` – Raynos Jun 16 '11 at 16:46
  • @Raynos: Yeah, I thought about that too, but I don't think it would be an issue in the real world and I hate introducing even more indirection... [Test case](http://jsbin.com/upeve4/2) But you are, of course, quite correct. – T.J. Crowder Jun 16 '11 at 16:49
3

My biggest problem with new is that it violates the Open/Closed principle. Because of the way that new manipulates the value of this, constructors are closed for extension, which means if you need to extend it, you must modify it, possibly breaking callers in the process.

Some examples of how factories can be extended that new doesn't allow:

  • Hide the details of object creation. (The rest of these are examples of why you might want to do that)

  • Store mutable prototypes and init functions on the factory object, accessible with this. (With new, this always refers to the newly created object).

  • Extend the pool of possible object types by allowing you to add capabilities to the factory object (extensible polymorphism).

  • Redefine the meaning of this during instantiation with .call() or .apply() (extending code-reuse).

  • Return a proxy to a new object in another memory space (iframe, window, different machine).

  • Conditionally return a reference to an existing object (Actually, you can do that with new and constructors, but then this is meaningless and misleading inside the constructor because a new instance gets allocated and then thrown away if you return something other than a new object).

  • Enable changes to instantiation strategy without breaking callers.

Note: I'm constantly irritated that I can't easily use any of these strategies with Backbone.js because it forces you to use new, unless you wrap it, but then you close off its standard inheritance mechanism. By way of contrast, I have never found myself irritated that jQuery uses a factory function to instantiate jQuery-wrapped DOM collections.

Eric Elliott
  • 4,711
  • 1
  • 25
  • 33