Hacker News new | past | comments | ask | show | jobs | submit login
Preparing Yourself for Modern JavaScript Development (codethinked.com)
206 points by Tenhundfeld on July 6, 2012 | hide | past | favorite | 71 comments



This is a nice, gentle introduction, but if you're going to start using modules you might as well go slightly farther and use something like RequireJS (http://requirejs.org/) or Browserify (https://github.com/substack/node-browserify).


And then migrate to native ES6 modules when they're implemented: http://wiki.ecmascript.org/doku.php?id=harmony:modules


both RequireJS and Browserify are not good options. Browserify's implementation is awkward, incomplete and pollutes global scopa a lot.

Check out OneJS: http://github.com/azer/onejs

It's the only tool that lets you structure your client-side project as a CommonJS package and produces unobtrusive code mixable with anything in same global scope.


There is another comment on this thread, by a separate user that reads:

"both RequireJS and Browserify are not even good options. Browserify's implementation is awkward, incomplete and pollutes global scope a lot. Check out OneJS"

I think there may be an agenda behind these comments.


I'm the owner of both comments and wrote it for two times because I can't see the other one and have very strong opinions about this topic.


(That other account has apparently been hell-banned, seemingly due to a downvotes during a political conversation about Kim Jong-il that you were involved in; you should probably stop using that account, as users can only see your posts if they turn on showdead. FWIW, thank you very much for this comment about OneJS, despite the flak you got for it due to the post from your hell-banned account.)


What's specifically wrong with RequireJS? I haven't heard much discussion of it, and you only addressed Browserify in your comment.


I've been following the work on RequireJS for two years and think that it's an evil project that sabotages the rise of CommonJS, NodeJS and NPM by leading client-side Javascript coders to follow a non-standard, awkward way of JS development. From the very beginning, it forces coders to cover their code with awkward code and maintain it manually. It's completely insane. I even think that I'm wasting my time by talking about it.


> it forces coders to cover their code with awkward code and maintain it manually

Friendly request for proof via examples. A gist would be fair.


here; http://requirejs.org/docs/

you can see all the smelling shit of requirejs there.


That's not really an answer. What _about_ their way of doing things do you consider to be wrong and why?


Can you expound please?


A unbaised post comparing the two would be nice. Sounds like a post Addy Osmani would write. I found this post(http://blog.millermedeiros.com/amd-is-better-for-the-web-tha...) talking about why the author thinks AMD is more flexible (better). Though a biased article(though I think the CJS approach to defining an AMD method is reasonable), it does show the syntax of the two approaches. I CJS syntax has less boilerplate, but is that due to fact that CJS loads synchronously? YES, I know processors like Onejs can make so CJS modules can be used in a browser(wonder is the processed result looks similiar to Requirejs?). Just wondering if Requirejs is designed the way it is because it took loading modules asynchronously into account from the begining.


Their (RequireJS's) website claims the following; I would be interested in an argued response/critique.

> CommonJS defines a module format. Unfortunately, it was defined without giving browsers equal footing to other JavaScript environments. Because of that, there are CommonJS spec proposals for Transport formats and an asynchronous require.


that's not true. They didn't even experiment CommonJS on browsers properly.


I always find it odd that the same developers who deride PHP look at things like IIFE and think "wow, JS is a cool, modern language!"

Thanks for the article though, if I ever need to write some JS this will help keep me a little more sane.


> I always find it odd that the same developers who deride PHP look at things like IIFE and think "wow, JS is a cool, modern language!"

I don't deride PHP (I don't know it), but I see nothing wrong with IIFE. Yes, it could benefit from some syntactic sugar, but otherwise it is just using one of the best features of JavaScript, function support (first-class functions, anonymous functions, etc.).

It makes perfect sense to use a pattern like IIFE in order to encapsulate code, often using closures to allow access in a controlled manner from the outside, etc.

I suppose perhaps you don't like closures and so forth, and that's fine, but many people including me love them.


IIFEs are cumbersome and non-intuitive. Better languages have block scope or a "let" statement instead.


Javascript [does/will]* have a "let" statement.

*depending on which version of ECMAscript is being used.


`let` is drafted for ECMAScript 6.


...and ECMAScript 4 had block scope.

Language design by committee is seriously flawed and until a feature is shipping in all the major browsers it isn't real.


ECMAScript 4 was cancelled.


That was exactly my point.


The first time I saw something like IIFEs was in one of the SICP lectures, where they show that variable definitions with let or define can be seen as syntactic sugar over immediately invoked lambdas.

And even those Scheme uber-nerds considered it big hack, more useful in theory then in practice.


It's useful as a de-sugaring step during compilation, not something people generally do by hand.


Continuation Passing Style is also supposed to be used as part of acompilation step but look at how everyone does in by hand in Javascript-land :(


For real. I tried to find a way to point that out without sounding snarky, gave up.


Exactly my thoughts. There really needs to be some healthy competition in client side languages.


Oh definitely. Or rather we need a vm designed to write code that can be optimized to run fast (personal dream, allow me to optionally include typing information. I already type my JS, might as well make it run better).

This means that the vm would be lowlevel enough that performance would start to approach that of native code, with complete freedom for the programmer to write the code for it in any language he desired.

The browser would then be able to execute this code instead of the corresponding Javascript file (e.g you compiler outputs two different object codes, one minified javascript for legacy browsers, one modern vm file for modern browsers).


You'll be disappointed to know that Dart doesn't use bytecode.


There's a wholly unconvincing argument as to why Dart isn't pushing a VM as well, http://www.dartlang.org/articles/why-not-bytecode/ . Might as well be titled "Why not the JVM?" in my opinion.


It isn't unconvincing. They make the case that bytecode is not assembly in reality, and constrains what you can do in languages compiled to it.


Nonsense, a VM is what it's defined to be. If you wanted the x86 (or ARM, or PPC, etc.) instruction set to be your bytecode, you've got it.

That article starts from the assumption that any in browser VM will resemble the JVM; which is a faulty assumption. In fact, any in browser VM would have to be very different from the JVM since Rule 0 for any such VM would be "acceptable Javascript performance".

From that article:

> Meanwhile, JVMs don't do these optimizations. Since all types are statically

> known, the compiler knows exactly how much storage they need and what

> operations they support. It can then generate appropriate tight code for those

> types. This is fast if your language is statically typed, but if you're trying to

> compile a dynamically typed language to the JVM, it won't be able to run as fast as a

> VM that can assume dynamic typing all the way down and optimize specifically

> for that.

A whole wasted paragraph. Of course a browser VM wouldn't be statically typed, that'd make compiling/running Javascript a bear. One would hope there'd be some system of type-hinting, because eliminating dynamic dispatch is really low hanging fruit in terms of optimization, but JVM/CLR-style static typing? Madness.


Dart isn't relevant, since google isn't pushing it nearly hard enough.


Here are a couple book recommendations for those of you looking to improve your JavaScript:

"JavaScript: The Good Parts" by Douglas Crockford "JavaScript Patterns" by Stoyan Stefanov


The Crockford book is 4 years old now, is it still considered to be a good book to pickup? I've done JS off and on for a while, but mostly haphazardly (with an emphasis on the hazard). I'm looking to start doing some backbone.js and jquery and a book to help start off with it.


Despite being a small book you can ignore a good third of it, and I think his constructor pattern is out of date? but - it does do a good job of explaining the actually idiosyncratic thing about js - the whole prototype chain thing, and the scoping.


Even though I recommended the book, I completely agree with you.


If four years would be enough to change a lot about the core JavaScript language, there wouldn't be a need for the book in the first place. It's still quite modern enough, and will be until a major EcmaScript overhaul is propagated and accepted by all common browsers…

It's not about modern web techniques, which is why it aged that well. For those, I'd personally recommend pulling apart some modern webapps or libraries, or reading modern tutorials. Usually you're somewhat outdated once your book reached its publisher… (Having said that, JavaScript Patterns is also pretty neat)


And if you want more of Crockford's advice, he's done a talk about ES5 changes (which you should really watch, they are great)


I've been working on following the good parts for my latest project. I've started to cringe any time people talk about the prototype or using "this"


That's unfortunate. Properly using prototypes and "this" is the only way to do high-performance JavaScript with objects.

Get cozy with them -- they're useful.


It isn't the only way, it's just that browser JS engines tend to be optimised for them.


I believe your statement is therefore equivalent to the preceding one.


I cringe when people claim to be writing more than the most basic JS and aren't using prototypes.


It's a style choice. You don't have to use classes and/or do anything with the prototype chain.


> It's a style choice

No. max line lengths is a style choice. indenting with spaces vs tabs is a style choice brace position is a style choice

Using prototypes and instances in JS is about using the right tool for the job.


That was a well-written article that has a pattern that should work well for a lot of people. But I think it can be improved.

One cool fact is that Javascript has first class functions. This means you can bring a lot of development patterns from the functional realm into your web development.

There is a trend of languages moving away from the concept of mutable state. The article does a good job with the first steps (by wrapping everything in its own function scope), but Javascript lets us go further. The article seems like it attempts to shoe-horn the concepts of classes and sub-type polymorphism into a language that has better options.


One time I saw a some code where the author re-implemented haskell in javascript. Needless to say reading that code was nearly impossible!


This is the kind of article I really needed to read 18 months ago instead of fumbling my way through it by myself.

It's amazing how few tutorials there are out there that fill in that gap between being a traditional web developer versed in .net / php and knowing the basics of javascript to being an actual javascript coder.

Most articles and tutorials around the web seem to target the beginner while most hacker news front page pieces target the advanced coder who already understands closures / prototypes etc and there seems to be very little in between.


I'm not a Javascript developer... but that IIFE syntax strongly reminds me of LISP.

(function(){//do some work})();


My "breakthrough moment" with Javascript was realizing that I could just treat it like scheme.


What is the difference between using the prototype technique shown in the article and this?

var Person = function (){ this.Save = function() { … }; };

var person = new Person(); person.Save();


That's called a "privileged method". The advantage of those is that they can access any private variables from the constructor, and they keep those variables in scope for their lifetime.

The disadvantage however, is that the method is re-created once for each instance when the constructor runs, so they are not as memory-efficient as the prototype style.

Also, when you add methods to the prototype you affect all instances from the past and the future. So for example if you add a method to String.prototype, all current and future strings get it.


Should I be concerned about the additional memory my way of doing things takes? It seems more convenient, allows for private methods and a limited drawback in my view (memory is rarely a bottleneck)


Probably depends on your application. You may also see slower construction time if you are creating thousands of objects.

I think which to use depends on whether you believe private methods/variables are helpful or not. Some argue they are unnecessary and it's better to keep everything public. Some like to use a naming convention like an underscore prefix on private items, but declare them publicly.

You could also argue that private items are problematic if you start composing classes from other classes by extending. Then the new methods can't access the private state because they're in a different scope, whereas if they were on the prototype they could find it through `this`.

I've used both approaches but nowadays tend towards the prototype style as that seems to be the more accepted.


There are also other problems such as nesting functions inside other functions inside other functions which is IMO much harder to maintain than a clean flat list of functions defined in the prototype. Don't even get me started on using "self", "that" or "me" instead of the built-in keyword "this".

It could be just me but I cannot understand how that was/is/has been acceptable at all in the first place. Doesn't functions nested 11 levels deep (true story) ring any alarm bells for people?


Memory is seldom a constraint on the sorts of computers which run JS. The real WTF about that approach later on is just going to be that person.hasOwnProperty('Save') === true. As long as you can guarantee that you will never want to iterate over a person with a for/in loop, you should be pretty much fine.


why private methods?


var Person = function() { var privateMethod = function() {...}; };


why: what's your reasoning for using them?

A more memory-efficient approach:

    var Person = (function(){
  
      function Person(x){
        this.x = x
      }
      
      function myPrivateMethod(x){
        //...
      }

      Person.prototype.something = function(){
        y = myPrivateMethod(this.x)
      }
    
      return Person 
    })()


Normally I use code like this to avoid polluting the global namespace -- you might as well throw your functions in there and they will be "private":

    (function(){ 

     })();


I'd be interested if one of these articles on modularity also addressed testability. For example, the IIFE pattern in the article:

(function(window, $, undefined){ //do some work }(window, jQuery));

If instead of immediately executing this function, we kept a reference to the function, then reference it, the function can be supplied with mocks for testing. Obviously there's an issue that you'd pollute the global namespace with this named function. So what's the best practice there?


I don't think you need to go through all the trouble. Some thing like the following should be enough:

    oldJQuery = jQuery;
    jQuery = mock;

    //Run module

    jQuery = oldJQuery;
Remember that IIFE's are basically equivalent to variable definition and assignment. The only reason we even have to go through the trouble of using them is due to JS not having block scope.


Yeah, I'm aware I can do that, but it gets a bit painful for code that is stateful & executes on load (like IIFE does). You end up having to write a separate test script to run in a separate process for each test case...way slower than if you can isolate the module and 'restart' it in a closure for each test.

A different approach that might work for me would be to subvert require.js, so that it pulls in mocks. I see there's other people thinking along the same lines: https://github.com/tigbro/requirejs-factory-plugin


great post, thanks.

i also highly recommend using popular frameworks and libraries, since lots of them use the tricks mentioned in your article.

Another thing that's worth noting is the JSON Object Notation: http://www.hunlock.com/blogs/Mastering_JSON_(_JavaScript_Obj...

very readable form of declaration.


"JSON Object Notation" => JavaScript Object Notation Object Notation



> Spy.prototype = new Person();

Don't do it that way. If the Person constructor assigns any data, for example an array, that data will be shared with all instances of Spy. Most likely you don't want that. Instead call the Person constructor within the Spy constructor:

  function Spy() {
    Person.apply(this, arguments);
  }

  Spy.prototype = Person.prototype;
  Spy.prototype.constructor = Spy;


Person will also have anything you add to Spy's prototype if you do that.

Attaching the "parent" prototype you want to have in the "child" prototype chain to a dummy constructor and new-ing that will do the trick for widest compatibility, although Object.create in ES5 and __proto__ (standardised) in ES6 both give you more direct access to the prototype chain:

  function inherits(child, parent) {
    var dummy = function() {}
    dummy.prototype = parent.prototype
    child.prototype = new dummy()
    child.prototype.constructor = child
  }

  function Spy() {
    Person.apply(this, arguments)
  }
  inherits(Spy, Person)


Dude, that's a pretty awful way to do it. After running your example code:

    (new Person).constructor === Spy




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: