Skip to content

Instantly share code, notes, and snippets.

@petermichaux
Created April 29, 2012 18:06
Show Gist options
  • Save petermichaux/2552304 to your computer and use it in GitHub Desktop.
Save petermichaux/2552304 to your computer and use it in GitHub Desktop.
JavaScript's new and map don't play well together
var todoViews = todoModels.map(function(todoModel) {
return new TodoView(todoModel);
});
// if Function.prototype.new is defined or TodoView.new is defined using "this"
var todoViews = todoModels.map(TodoView.new, TodoView);
// if Todo.new is defined without using "this"
var todoViews = todoModels.map(TodoView.new);
@isaacs
Copy link

isaacs commented Apr 29, 2012

If you have use instanceof guard, then it's even easier:

function TodoView (model) {
  if (!(this instanceof TodoView)) return new TodoView(model)
  // ...
}

var todoViews = todoModels.map(TodoView)

@cjohansen
Copy link

While the instanceof guard works just fine, it smells to me. Too much hacking around quirky behavior.

@petermichaux
Copy link
Author

I agree with Christian. It works but I wouldn't want it to be part of my programs.

@isaacs
Copy link

isaacs commented Apr 29, 2012

I find that turning my classes into generators simplifies enough cases to be worthwhile. (For example, this example ;)

@cjohansen
Copy link

What do you mean by 'generator'? Your previous example?

@isaacs
Copy link

isaacs commented Apr 29, 2012

Oh, sorry, s/generator/factory/. Typo :)

@cjohansen
Copy link

Still - as in your previous example? I'd argue both the constructor with the instanceof "trick" and Peter's Thing.new (or as I've been preferring, thing.create) can be considered factories. :)

@marcoos
Copy link

marcoos commented Apr 29, 2012

ES.next:

var todoViews = todoModels.map(model => new TodoView(model));

@cjohansen
Copy link

@marcoos I'm sure we'll enjoy that in 5 to 10 years ;)

@robotlolita
Copy link

The problem with instanceof on constructors is that you have to throw yourself in inheritance madness rather than composition. No more applying a constructor to another object just for initialisation — in fact, I've always found the thing about having to couple creating/cloning objects and initialisation stupid.

@jfromaniello
Copy link

What about this:

Object.defineProperty(Function.prototype, "new", {
  enumerable: false,
  value: function() { 
      return new this; // or maybe: this.apply(this, arguments); 
   }
});

Then you can do

var todoViews = todoModels.map(TodoView.new)

@isaacs
Copy link

isaacs commented Apr 29, 2012

@jfromaniello Nope. Not unless you do TodoView.view.bind(TodoView). Otherwise this will be lost.

@petermichaux
Copy link
Author

I just posted a Function.prototype.new...

https://gist.github.com/2489714

@abozhilov
Copy link

As @isaacs pointed out var todoViews = todoModels.map(TodoView.new); is not sufficient to do the job. You should bind the new method with specific constructor. You also could use factory method, which returns function similar to new:

var todoViews = todoModels.map(TodoView.factory());

https://gist.github.com/2553803

@petermichaux
Copy link
Author

Corrected initial example to account for "this".

@isaacs
Copy link

isaacs commented Apr 29, 2012

You could also do this:

Object.defineProperty(Function.prototype, "new", {
  enumerable: false,
  get: function () {
    return function () {
      var p = Object.create(this.prototype)
      var ret = this.apply(p, arguments)
      return (ret && typeof ret === 'object') ? ret : p
    }.bind(this)
  }
})

Then Function.prototype.new will work, since it'll bind at the time of getting.

> function Foo () { console.error(this instanceof Foo) }
> f = Foo.new()
true
{}
> var FooNew = Foo.new
undefined
> f = FooNew()
true
{}
> f instanceof Foo
true
> 

@robotlolita
Copy link

@isaacs That is pretty Pythonic.

@cjohansen
Copy link

Hah, the "bind on get" trick is pretty neat. I thought I'd throw in my suggestion as well. I don't care much for constructors and think that objects and Object.create (or a shim) yields a structure that reads better and is easier to follow. My experience from teaching courses in JavaScript also seems to indicate that this approach is easier for newcomers to adopt - especially if inheritance is required (which, ironically, I find it rarely is): https://gist.github.com/2555903

@jfromaniello
Copy link

jfromaniello commented Apr 30, 2012 via email

@isaacs
Copy link

isaacs commented Apr 30, 2012

@cjohansen I think the "easier for beginners" argument is a fine one if you're tasked with making a bunch of beginners productive as fast as possible. As it happens, in my own life, I am not a beginner, nor do I share in code creation/maintenance with very many beginners, so that argument is not so compelling.

Furthermore, in my experience, most people spend a short time being a beginner, and then a long time being experts. By the nature of the time spent in each state, there will tend to be more experts than beginners, just as there are typically more adults than children. You certainly wouldn't suggest that cars should be illegal simply because it is difficult for a child to learn to drive!

Therefor, for me at least, it seems better to optimize to minimize the mistakes that experts continue to make, and use patterns that will be the most convenient for experts to maintain.

And becoming an expert is not so hard, really. It just takes a thousand hours or so of JavaScript use (assuming that the person already is a decent programmer in some other language). It's a lot easier than becoming a competent dentist or hairdresser or lawyer, and there are plenty of dentists and hairdressers and lawyers.

@cjohansen
Copy link

@isaacs You don't have to preach to me about not optimising for beginners :) I agree with everything you say. I'm sorry if it came off that way - it was merely an additional observation. With that said, teaching constructors and inheritance in JavaScript is a bit of a nightmare...

I dislike constructors for many reasons. One is the case in this gist. Like I said before, the instanceof trick works, but feels very hacky to me. I dislike the fact that every function is a constructor, depending on the caller, I dislike inheritance with constructors (even if you can abstract the mess away), and I dislike the detached nature of constructor + prototype definition. I also find instanceof to be pretty useless, so I'm not very interested in establishing the "identity" of my objects through the constructor.

In summary, to me, constructors offer a very awkward way of allocating and initialising objects and no real benefits (over e.g Object.create or functions that return object literals).

In my approach functions are functions, and objects are objects. I like the clean separation. But I guess some of this is just bikeshedding, and what one prefers depends highly on taste and habits.

@isaacs
Copy link

isaacs commented Apr 30, 2012

the instanceof trick works, but feels very hacky to me.

Yep. No one said that JavaScript was the prettiest language :) The reason I like this approach is that even though it's hacky in one spot, it's generally less hacky everywhere else. If you're not creating bunches of these things, then why bother using a class at all?

constructors offer a very awkward way of allocating and initialising objects and no real benefits

Well, it's just a standard and convenient way to do "set up this object" logic, which is sometimes nice. It's also crazy optimized in v8 (and, I imagine, other implementations as well.)

@cjohansen
Copy link

It's also crazy optimized in v8 (and, I imagine, other implementations as well.)

Using a shim in place of Object.create (which is everything but crazy optimized at this point) you are basically still using constructors, just with one additional function call. Most code-bases can handle that :)

why bother using a class at all?

Good question. Sometimes a function returning an "ad hoc object" is the right solution. However, if there are more than one instance I do like the fact that function objects are inherited, not duplicated, and "classes" (just objects in my case) lend themselves better to extension. I think we agree in most practical senses except for how to define these "classes".

My dream solution would be if ES6 introduced classes that don't rely on keywords (like new) and removed constructors. But that's not gonna happen, so with ES6 (if it ever materialises) we'll have yet another way of creating objects. :(

@kaisellgren
Copy link

I've occasionally done that instanceof "trick", but it's quite easy to forget and I've decided that if you forget your "new" (provided that you even use it for inheritance), then it's your problem.

@isaacs The reason I like this approach is that even though it's hacky in one spot, it's generally less hacky everywhere else."

This is what I've observed too... put a hack in there, less hacks elsewhere. :P

@robotlolita
Copy link

@cjohansen To back up @isaacs claim on the performance: http://jsperf.com/object-creation-access/2

I do feel the simplicity of a more direct mapping between the delegative prototypical OO model and the primitives used, which in this case would amount to using Object.create + some abstractions on top of it, to lead to code that is easier to follow as well. In which case, I don't really care about the performance differences between constructors and the other means of creating objects — that is, of course, unless you need to create millions of objects per second, but then you'll be fighting the GC anyways.

@medikoo
Copy link

medikoo commented Apr 30, 2012

Object.create unfortunately is magnitudes slower than new in some engines. It's rather an engines (not specification) issue, so hopefully it'll get better. Anyway for now I would use Object.create only for prototype extensions (e.g. Animal to Dog), and stick to new when creating object instances (e.g. Dog to sparky).

@cjohansen
Copy link

FTR: I'm aware of the issues with Object.create, and a simple solution is to use a constructor-based shim (ignore the descriptor argument). I don't think a poorly performing native Object.create is a good argument against basing a design on just objects and Object.create-like construction.

@medikoo
Copy link

medikoo commented Apr 30, 2012

@cjohansen I had noticeable issues on V8 engine (which implements Object.create natively), what would you propose in that case? Also it's not that better for shims, see: http://jsperf.com/object-create-vs-crockford-vs-jorge-vs-constructor/31

It's not great argument but sometimes poor performance asks for refactoring back to new (I've gone that path in one of the projects).

@cjohansen
Copy link

@medikoo On Firefox, Object.create comes out the fastest. V8 obviously has some insane optimization for the bare constructor use (as mentioned before). I don't really worry about it...

@medikoo
Copy link

medikoo commented Apr 30, 2012

@cjohansen on Firefox performance of Object.create vs new is not really different (it's slightly faster on benchmark that's it), on V8 it is noticeable in real world. Hopefully one day V8 will have it fixed. I also do not tend to worry about things prematurely ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment