3

Looking into the todomvc backbone codes example. The structure in the js/ fold:

├── app.js
├── collections
│   └── todos.js
├── models
│   └── todo.js
├── routers
│   └── router.js
└── views
    ├── app-view.js
    └── todo-view.js

app.js

var app = app || {};
$(function () {
    'use strict';
    // kick things off by creating the `App`
    new app.AppView();
});

collections/todos.js

var app = app || {};

(function () {
    'use strict';
    var Todos = Backbone.Collection.extend({
    model: app.Todo,
    app.todos = new Todos();
})();

models/todo.js

var app = app || {};

(function () {
    'use strict';
    app.Todo = Backbone.Model.extend({
    });
})();

views/app-view.js

var app = app || {};
(function ($) {
    'use strict';
    app.AppView = Backbone.View.extend({
})(jQuery);

I have two questions:

  1. why var app = app || {} in each file?

  2. What are the differences between $(function(){}), (function(){})(), and (function($))(jQuery)?

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
BAE
  • 8,550
  • 22
  • 88
  • 171

2 Answers2

4
  1. app variable is global and encapsulates entire Backbone application to minimize global namespace pollution. Here you can find more details about Namespacing Patterns.

    var app = app || {} initializes global app variable with new empty object if it is not initialized yet. Otherwise it will be untouched.

  2. The functions:

    • $(function(){}) is a shortcut for jQuery's $(document).ready(function(){}). Docs
    • (function(){})() is an Immediately-invoked function expression (IIFE) without parameters
    • (function($){ /* here $ is safe jQuery object */ })(jQuery) is IIFE with parameter - jQuery object will be passed as $ into that anonymous function

$(function() {
  console.log("Document ready event");
});

$(document).ready(function() {
  console.log("Document ready event");
});

(function() {
  console.log("Immediately-invoked function expression without parameters");
})();

(function($) {
  console.log("Immediately-invoked function expression with parameter. $ is a jQuery object here:");
  console.log($.fn.jquery);
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
Yurii Semeniuk
  • 933
  • 8
  • 13
  • 1
    The real name of self-invoking function is IIFE for [_Immediately-invoked function expression_](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression). – Emile Bergeron Oct 31 '16 at 20:36
  • 1
    Your first point is also incorrect, `var app = app || {}` only serves as a namespace for the app, to avoid polluting the global namespace, but the order is still important as you can't use something that isn't defined yet. – Emile Bergeron Oct 31 '16 at 20:39
  • Thank you for your comment. Renamed to IIFE. I used name from w3schools.com – Yurii Semeniuk Oct 31 '16 at 20:45
  • About first point `app` is actually global and it pollutes the global namespase. In this ToDo example it even is marked with comment `/*global Backbone */` : https://github.com/tastejs/todomvc/blob/gh-pages/examples/backbone/js/models/todo.js – Yurii Semeniuk Oct 31 '16 at 20:48
  • It's more that it limits its polluting to one variable, instead of everything being global. – Emile Bergeron Oct 31 '16 at 20:51
  • Ok, added this note to the answer :) – Yurii Semeniuk Oct 31 '16 at 20:56
  • Now I'm reading *'JavaScipt Patterns'* book by *Stoyan Stefanov* and he uses `self-invoking` and `self-executing` names for IIFE. I think it's not so important how to call this – Yurii Semeniuk Oct 31 '16 at 21:18
  • Yeah, you were correct already with self-invoking, I was just pointing out the full name. Most JavaScript developer will refer to IIFE which is the most descriptive name in the JavaScript context. – Emile Bergeron Oct 31 '16 at 21:20
4

While Yurii explained the difference between all the patterns, it's missing the "why" you would need these.

Namespacing and scoping

The overrall goal of the following patterns is mostly namespacing and scoping, with different benefits. It's a good practice to avoid polluting the global namespace, and since JavaScript doesn't have namespace as a core feature, other patterns have emmerged to solve that.

See How do I declare a namespace.

Global namespace

var app = app || {}; // if it doesn't exist yet, make it an new object.

In order to avoid polluting the global namespace (AKA making everything a global variable), you create only one variable, inside of which you insert every other modules of your app.

Then, each file exports its module into that sole global variable.

Note that the order of the files is still important if a module depends on another.

If we look at the TodoMVC example, they included the files in a specific order:

<script src="js/models/todo.js"></script>
<script src="js/collections/todos.js"></script>
<script src="js/views/todo-view.js"></script>
<script src="js/views/app-view.js"></script>
<script src="js/routers/router.js"></script>
<script src="js/app.js"></script>

Scoping

Imagine you declared var test = 2; in a file and it's a critical variable that is used throughout that module. Then, in another file, you copy the good pattern you were using inside the first module. You've just overriden the test variable and now, it is involontary shared between two modules.

In order to have local functions and variables private to a module, you can scope them with an Immediately-invoked function expression (IIFE). Block scoping is relatively new and not well-supported yet, so the safest way is to use the function scope.

var app = app || {}; // global

(function () {
    // private to this scope
    var Todos = Backbone.Collection.extend({});

    // export the Todos constructor to the global app namespace
    app.Todos = Todos;

    function localFunction(param) { /** snip **/ }
})();
Community
  • 1
  • 1
Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
  • If you use `$(document).ready` to init your JS then order of JS files isn't important because at that moment all files will be downloaded and executed by JS engine. Actually `app.js` inits Backbone app when DOM is ready – Yurii Semeniuk Oct 31 '16 at 21:28
  • 1
    @YuriiSemeniuk only [app.js](https://github.com/tastejs/todomvc/blob/gh-pages/examples/backbone/js/app.js). The other files are inside IIFE and are executed immediately. – Emile Bergeron Oct 31 '16 at 21:29
  • 1
    I've fixed my answer. Thank you – Yurii Semeniuk Oct 31 '16 at 21:37
  • Since anyway we need to include JS-files in specific order then why not just create some `core.js` file and in it declare the global `app` object, include this file first and omit `var app = app || {}` in every following file? – Yurii Semeniuk Nov 01 '16 at 13:45
  • 1
    @YuriiSemeniuk files without any dependency within your modules (like a Backbone model), may be included in any order but still before they're needed (like before the collection that uses it), that's where the `var app = app || {};` is useful. It also helps linters which don't know about other files by default. – Emile Bergeron Nov 01 '16 at 14:20
  • @EmileBergeron Both answers are good. thanks. I just selected one as answer and up-voted the other. – BAE Nov 03 '16 at 16:00
  • @BAE yes, totally fair, Yurii fixed what made me comment, so his answer is good. – Emile Bergeron Nov 03 '16 at 16:03
  • @EmileBergeron an extrq question, here app === window.app? – BAE Nov 05 '16 at 16:40
  • @BAE yes, you can inspect the `window` object with the dev tool console and see all the globals – Emile Bergeron Nov 05 '16 at 16:52