5

Note: This is not a question about classical and prototype inheritance. It's about what coding patterns to use to initialize objects. Class constructors create and initialize objects, whereas avoiding the new operator and going for Object.create() only creates the object and sets up the prototype chain. I have not yet found an online resource that explains best-practice coding patterns for doing creation and initialization when going for the Crockford Object.create() approach.

If I have a constructor (in my head this makes my class, although I know classes don't technically exist yet in mainstream JavaScript)

function Person(first, last) {
  this.name = {
    first: first,
    last: last
  };
}

Person.prototype.tellName = function() {
  return this.name.first + ' ' + this.name.last;
}

I can then instantiate it like this

var p1 = new Person('John', 'Doe');
var p2 = new Person('Sven', 'Svensson');

And change the Person.name.first and Person.name.last separately

p1.tellName();  // Output: 'John Doe'
p2.tellName();  // Output: 'Sven Svensson'

p1.name.first = 'Peter';
p2.name.last = 'Celery';

And execute the object's function Person.tellName() with the following output

p1.tellName();  // Output: 'Peter Doe'
p2.tellName();  // Output: 'Sven Celery'

Which is very similar to how I'd approach building such a class in C++ or Java.


What patterns do I use to construct, or initiate an object where I can assign to a nested object when using the Crockford (Object.create()-ish) approach instead of new?

E.g.

...
// some code that does the same stuff as defining a class + constructor
...

var p1 = ???????
var p2 = ???????

// The following is the code and behavior I'm looking to get
p1.tellName();  // Output: 'John Doe'
p2.tellName();  // Output: 'Sven Svensson'

p1.name.first = 'Peter';
p2.name.last = 'Celery';

p1.tellName();  // Output: 'Peter Doe'
p2.tellName();  // Output: 'Sven Celery'
marcusstenbeck
  • 735
  • 6
  • 15
  • like this ? http://stackoverflow.com/questions/2709612/using-object-create-instead-of-new?rq=1 – Hacketo Jan 09 '15 at 10:45
  • I'm pretty sure I've seen this exact question before. – Benjamin Gruenbaum Jan 09 '15 at 10:46
  • Here: http://stackoverflow.com/q/27843436/1348195 – Benjamin Gruenbaum Jan 09 '15 at 10:47
  • @BenjaminGruenbaum: No, the way I worded that question was regarding inheritance. I accepted an answer that solved the questions I asked, but the problem I was trying to solve needed another question to be answered; which has to do with "constructor patterns" and initialization, as the classical constructor also initializes object properties (excuse me if I'm not using the correct technical terms, feel free to correct me). – marcusstenbeck Jan 09 '15 at 10:50
  • I'll open it and write an answer because you seem like keeping it closed would be against your will - but I honestly think it's pretty similar. – Benjamin Gruenbaum Jan 09 '15 at 10:52
  • `Person` is not a *class*, it is a `constructor`. – KooiInc Jan 09 '15 at 10:59
  • @KooiInc he can call it a class or a type or whatever there is no harm in that there is no strict terminology regarding that. – Benjamin Gruenbaum Jan 09 '15 at 11:05
  • @KooiInc I updated the question to clarify. Is it technically correct now? – marcusstenbeck Jan 09 '15 at 11:05
  • @marcusstenbeck: yep, looking good. See also: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model – KooiInc Jan 09 '15 at 11:09

1 Answers1

3

Instead of having:

function Person(first, last) {
  this.name = {
    first: first,
    last: last
  };
}

Person.prototype.tellName = function() {
  return this.name.first + ' ' + this.name.last;
}

You'd just have:

function Person(first, last) {
  return {
    name: { first: first, last: last },
    tellName: function() { return this.name.first + ' ' + this.name.last; }
  };
};

Or, if you prefer how person.create() looks, then:

var person = {
  create: function(first, last) {
    return {
      name: { first: first, last: last },
      tellName: function() { return this.name.first + ' ' + this.name.last; }
    };
  }
};

But in the second case you'd have an unnecessary object (person) containing only one function (person.create()).

No need for Object.create nor new since those are for inheritance which you said you do not care about. This would let you do:

var p1 = Person('John', 'Doe');
var p2 = Person('Sven', 'Svensson');

A fun fact is that you can still use new in front of person.create this way if you want but it would offer no effect. If you have to use the existing function you can set the this context explicitly by using .call

// with your original `Person`
var p1 = Person.call({}, 'John', 'Doe');
var p2 = Person.call({}, 'Sven', 'Svensson');

This would not set the prototype either since the function is not called like a constructor. See this answer about what prototypical answer does and doesn't do - in a line it's about sharing functionality it's not about constructing properties of your objects.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Thanks. I guess I'm having difficulty pinpointing how to articulate the question I'm trying to ask. Maybe some context helps. I've seen a transition where people use `var p = person.create('John', 'Doe');` instead of `var p = new Person('John', 'Doe');`. – marcusstenbeck Jan 09 '15 at 11:01
  • @marcusstenbeck you can put a `create` function on objects (like a static in other languages) instead of a constructor - that's not really related to `Object.create` at all. The advantage is it's just a regular function so you can compose it with other functions more easily. – Benjamin Gruenbaum Jan 09 '15 at 11:03
  • I understand. So how do you suggest I improve the question in order to arrive at an answer that addresses how initialization is commonly done? Doing it with `new` and a constructor does it automatically. I guess I've conflated the initialization routine with using `Object.create()`. I think there are others that might have this problem when running into the "don't use `new`" debate, and thinking "oh, there's another way of doing things … how?". – marcusstenbeck Jan 09 '15 at 11:13
  • @marcusstenbeck you should probably just read "JavaScript: The Good Parts" - it's a short and good book and it covers how initialization works and it's from the "`new` is evil `this` is evil camp". – Benjamin Gruenbaum Jan 09 '15 at 11:16
  • I updated the question to more closely reflect what I'm looking for. Is there some detail I've omitted or missed? (Also, I'll go ahead and buy the book.) – marcusstenbeck Jan 09 '15 at 18:54