18

Just finished reading Crockford's "JavaScript: The Good Parts" and I have a question concerning his stance on the psuedo-classical vs. prototypal approaches. Actually I'm not really interested in his stance; I just want to understand his argument so I can establish a stance of my own.

In the book, Crockford seems to infer that constructor functions and "all that jazz" shouldn't be used in JavaScript, he mentions how the 'new' keyword is badly implemented - i.e. non-Constructor functions can be called with the 'new' keyword and vice versa (potentially causing problems).

I thought I understood where he was coming from but I guess I don't.

When I need to create a new module I would normally start of like this:

function MyModule(something) {
    this.something = something || {};
}

And then I would add some methods to its prototype:

MyModule.prototype = {
    setSomething : function(){},
    getSomething : function(){},
    doSomething : function(){}
}

I like this model; it means I can create a new instance whenever I need one and it has its own properties and methods:

var foo = new MyModule({option1: 'bar'});
// Foo is an object; I can do anything to it; all methods of the "class"
// are available to this instance.

My question is: How do I achieve the above using an approach more suited to JavaScript? In other words, if "JavaScript" were a person, what would she suggest?

Also: What does Crockford mean when he says a particular design pattern "is more expressive" then another?

James
  • 109,676
  • 31
  • 162
  • 175

4 Answers4

10

See: Is JavaScript's “new” Keyword Considered Harmful?

It's important to remember that Crockford, like so many other JavaScript programmers, first approached the language with an eye toward "fixing" it - making it more like other (so-called "classical") OO languages. So a large amount of structural code was written, libraries and frameworks built, and... then they started to realize that it wasn't really necessary; if you approach JS on its own terms, you can get along just fine.

Community
  • 1
  • 1
Shog9
  • 156,901
  • 35
  • 231
  • 235
  • Thank you! Reading through that thread has given me every bit of info I required (especially your answer)! – James Apr 26 '09 at 17:19
4

The prototypal variant for the example you have looks like the following in my understanding:

Object.beget = function (o) { /* Crockfords replacement for the new */ }

var myModule = {
    something : null,
    getSomething : function () {},
    setSomething : function () {},
    doSomething : function () {}
};

And then you can do:

var foo = Object.beget(myModule);
foo.something = bar;

UPDATE: You can also use the builder pattern to replace a constructor like this:

var myModuleBuilder = {
    buildMyModule : function (something) {
        var m = Object.beget(myModule);
        m.something = something || {};
        return m;
    }
}

so then you can do:

var foo = myModuleBuilder.buildMyModule(something);
artemb
  • 9,251
  • 9
  • 48
  • 68
2

Your implementation is problematic because you're replacing the entire prototype object, losing the properties of the inherited function prototype and it would also break, or at least make it more difficult, the ability to make use of inheritance later on if you wrote other classes the same way.

A method more suited to Javascript would be:

var MyClass = function (storeThis) {
 this.storage = storeThis
}

MyClass.prototype.getStorage = function (){
   return this.storage;
}

MyClass.prototype.setStorage = function (newStorage){
  this.storage = newStorage;
}

Use it:

var myInstance = new MyClass("sup");
alert("myInstance storage: " + myInstance.getStorage());
myInstance.setStroage("something else");

As for the 'new' keyword and Crawford's problems with it, I can't really answer, because I haven't read the book, but I can see how you could create a new object by calling any function with the new keyword, including functions that are supposed to be methods of a class.

And when someone says something, such as a design pattern, is more 'expressive', he or she generally means that the design pattern is clear and simple to understand as to what it is achieving and how.

Bjorn
  • 69,215
  • 39
  • 136
  • 164
  • Thank you for your answers. My example wasn't really important, it was the design pattern itself I was enquiring about. Thanks for explaining "expressive" :) – James Apr 26 '09 at 16:51
  • Your first point is vague and seems misleading. What is this "built in functionality associated with every object that you're overwriting" that the replaced prototype has that J-P's replacement prototype doesn't? – Tim Down Oct 03 '09 at 23:23
  • Well I worded it badly, but a prototype derived from a function has properties and attributes that an object literal doesn't have. The toString for example. – Bjorn Oct 04 '09 at 08:50
  • Not true. The prototype for a function starts out as an object as if created with `new Object()`. And an object created with `{}` does have a `toString` method. Try `({}).toString()`, for example (parentheses around the object literal to ensure it is interpreted as an object rather than a block) – Tim Down Oct 04 '09 at 10:58
  • Hrm, I know they both have a toString method. My point was that they did different things. I just tried setting an object literal to a function's prototype and it didn't overwrite it like I thought it would. – Bjorn Oct 05 '09 at 20:30
0

Javascript isn't a person so she can't really suggest what you do.

None of the answers above have mentioned the plain-old functional style of inheritance, which I tend to find is the simplest.

function myModuleMaker(someProperty){
  var module = {}; //define your new instance manually

  module.property = someProperty; //set your properties

  module.methodOne = function(someExternalArgument){
    //do stuff to module.property, which you can, since you have closure scope access
  return module;
  }
}

Now to make a new instance:

var newModule = myModuleMaker(someProperty);

You still get all the benefits of pseudoclassical this way, but you suffer the one drawback that you're making a new copy of all your instance's methods every time you instantiate. This is probably only going to matter when you start having many hundreds (or indeed, thousands) of instances, which is a problem most people seldom run into. You're better off with pseudoclassical if you're creating truly enormous data structures or doing hard-core animations with many, many instances of something.

I think it's hard to argue that either method is "bad practice" per se.

Community
  • 1
  • 1
RP-3
  • 684
  • 4
  • 22