JS Best Practices 



Navigate with Esc, and
# My Goal Higher code qualitiy. Pragmatic solutions. No fancy stuff.
# Know Your
MS Tools ![tools](images/tool.png)
* Visual Studio 2010/2012 * [JScript Editor Extensions](http://visualstudiogallery.msdn.microsoft.com/872d27ee-38c7-4a97-98dc-0d8a431cc2ed) * [Resharper 7.1](http://www.jetbrains.com/resharper/whatsnew/#javascript) * [JSHint](http://www.jshint.com/platforms/) * [Chutzpah](http://chutzpah.codeplex.com/) * [Firebug](https://getfirebug.com/) / F12
# Know the
pitfalls ![pitfalls](images/banana_peel.png)
## Implied globals Forgetting var ```javascript contenteditable var foo = function() { bar = 1; }; ```
## Boolean type conversion To Truthy or to Falsy. That is the *only* question! ```javascript contenteditable var el = document.getElementById('does_not_exist'); if(el == false) { alert("shouldn't we see this message?!"); } ```
## Trailing comma works on my machine! ```javascript contenteditable var foo = { bar: "bar", baz: "baz", }; ```
## Return undefined señor developers wear mustaches { ```javascript contenteditable var foo = function() { return { x : "looks like C# now!" }; } ```
## Associative arrays they don't exist ```javascript contenteditable var x = []; x['foo'] = "bar"; ```
## try .. catch .. finally who cares about the reason? ```javascript contenteditable var foo = function() { try { doCrazyStuff; } catch (e) { return false; } return true; }; ```
## for .. in use a framework ```javascript contenteditable var data = { foo: "oh", bar: "my" } for (var d in data) { console.log(data[d]); } ```
## for .. in & never touch Object.prototype ```javascript contenteditable Object.prototype.yetAnotherToString = function() { return "god"; } ```
## Hoisting declare upfront all variables ```javascript contenteditable var foo = "global"; var bar = function() { alert(foo); var foo = "local"; alert(foo); }; ```
## Eval ... and the job is done ```javascript contenteditable function poorMansJsonParser(text) { return eval("(" + text + ")"); } var text = ' { "hello" : "world" } '; var json = poorMansJsonParser(text); ```
## Eval is evil! Never ever! ```javascript contenteditable var text = ' function() { alert("hacked!"); })( '; ```
## Globals the mother of all antipatterns ```javascript contenteditable function foo() { return "bar"; } console.log(this['foo']()); ```
![kitteh](images/kitteh.jpg) Every time you clutter the global namespace, somewhere in the world a helpless kitten dies!
# Pretty Code ![tools](images/mustache.png)
## Coding conventions
  1. indentation (4 spaces!)
  2. curly braces everywhere
  3. semicolons everywhere
  4. constructor functions: UpperCamelCase
  5. all other: lowerCamelCase
## Globals reduce, minimize, delete or kill them ```javascript contenteditable (function() { "wtf?" })(); ```
## The switch-case syndrome a functional language wants functions! ```javascript contenteditable switch (something) { case 1: doFirst(); break; case 2: doSecond(); break; case 3: doThird(); break; } ```
## Lookup tables avoid the switch-case syndrome ```javascript contenteditable var methods = { 1: doFirst, 2: doSecond, 3: doThird }; if (methods[something]) { methods[something](); } ```
## Inheritance favour composition over inheritance (FCoI)
Because inheritance exposes a subclass to details of its parent's implementation, it's often said that 'inheritance breaks encapsulation'.
(Gang of Four 1995:19)
## Revealing Module Pattern ```javascript contenteditable fragment var myRevealingModule = function () { var _name = "Johannes"; function greetings() { console.log("Hello " + _name); } function setName(name) { _name = name; } return { setName: setName, greetings: greetings }; }(); ``` [» Documentation](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript)
## Modul loaders use AMD (require.js) ```javascript contenteditable fragment define('test', ['jquery'], function() { return { saySomething : function() { alert("hello!"); } } }); require(['test'], function(t) { t.saySomething(); }); ```
## Events Publish/Subscribe Pattern ```javascript contenteditable var $events = $({}); $events.bind('somethingHappens', function() { alert("Something happened!"); }); $events.trigger('somethingHappens'); ```
![Jasmine](images/jasmine_logo.png") _TDD with [Jasmine](http://pivotal.github.com/jasmine/)_
## Why Jasmine?
  •     BDD-style     similar to JSpec or RSpec,
    created by authors of jsUnit and Screw.Unit
  •  independent  from any browser, DOM,
    framework or host language
  •    integrates     into continuous build systems
## Jasmine Bootstrap ```html contenteditable small _LT_!DOCTYPE html_GT_ _LT_html_GT_ _LT_head_GT_ _LT_title_GT_Jasmine Spec Runner_LT_/title_GT_ _LT_link rel=_QUOT_stylesheet_QUOT_ href=_QUOT_lib/jasmine-1.3.1/jasmine.css_QUOT_ /_GT_ _LT_script src=_QUOT_lib/jasmine-1.3.1/jasmine.js_QUOT__GT__LT_/script_GT_ _LT_script src=_QUOT_lib/jasmine-1.3.1/jasmine-html.js_QUOT__GT__LT_/script_GT_ _LT_!-- include source files here... --_GT_ _LT_script src=_QUOT_src/Player.js_QUOT__GT__LT_/script_GT_ _LT_script src=_QUOT_src/Song.js_QUOT__GT__LT_/script_GT_ _LT_!-- include spec files here... --_GT_ _LT_script src=_QUOT_spec/SpecHelper.js_QUOT__GT__LT_/script_GT_ _LT_script src=_QUOT_spec/PlayerSpec.js_QUOT__GT__LT_/script_GT_ _LT_script_GT_ (function () { var htmlReporter = new jasmine.HtmlReporter(); var jasmineEnv = jasmine.getEnv(); jasmineEnv.addReporter(htmlReporter); jasmineEnv.specFilter = function (spec) { return htmlReporter.specFilter(spec); }; var currentWindowOnload = window.onload; window.onload = function () { if (currentWindowOnload) { currentWindowOnload(); } jasmineEnv.execute(); }; })(); _LT_/script_GT_ _LT_/head_GT_ _LT_body_GT_ _LT_/body_GT_ _LT_/html_GT_ ```
## Output
## Hello World ```javascript contenteditable var helloWorld = function() { return "Hello World!"; }; describe('helloWorld', function() { it('says hello', function() { expect(helloWorld()).toEqual("Hello World!"); }); }); jasmine.getEnv().execute(); ``` hint: press F12 and paste this code!
## Matchers ```javascript contenteditable expect(x).toEqual(y); expect(x).toBe(y); expect(x).toMatch(pattern); expect(x).toBeDefined(); expect(x).toBeUndefined(); expect(x).toBeNull(); expect(x).toBeTruthy(); expect(x).toBeFalsy(); expect(x).toContain(y); expect(x).toBeLessThan(y); expect(x).toBeGreaterThan(y); expect(function(){fn();}).toThrow(e); ```
## Own matchers ```javascript contenteditable beforeEach(function () { this.addMatchers({ isACat: function () { return this.actual.isFluffy() _AMP__AMP_ this.actual.isLazy(); } }); }); describe('Garfield', function () { it('is a cat', function () { expect(new Garfield()).isACat(); }); }); ``` [» Documentation](https://github.com/pivotal/jasmine/wiki/Matchers)
## Test-Driven Development 1. Write your tests 1. Watch them fail 1. Make them pass 1. Refactor 1. Repeat

see [Growing Object-Oriented Software, Guided by Tests](http://www.amazon.de/dp/0321503627), page 6
see [Working Effectively with Legacy Code](http://www.amazon.de/dp/0131177052), page 62 or many other
## 1. Write your test ```javascript contenteditable describe("saveFormat", function () { var original = '{0} - {1} - {2}'; it("should replace placeholders", function () { var expected = 'A - B - C'; var formated = saveFormat(original, 'A', 'B', 'C'); expect(formated).toEqual(expected); }); it("should encode injected content", function () { var expected = 'A - _AMP_lt;b_AMP_gt;TEST_AMP_lt;/b_AMP_gt; - C'; var formated = saveFormat(original, 'A', '_LT_b_GT_TEST_LT_/b_GT_', 'C'); expect(formated).toEqual(expected); }); }); ```
## 2. Watch them fail ```javascript contenteditable var saveFormat = function() { return "boo!"; }; jasmine.getEnv().execute(); ``` Demo
## 3. Make them pass ```javascript contenteditable var saveFormat = function(txt) { $(arguments).each(function (i, item) { if (i _GT_ 0) { item = ($('_LT_div/_GT_').text(item).html()); txt = txt.replace("{" + (i - 1) + "}", item); } }); return txt; }; jasmine.getEnv().execute(); ``` Demo
## 4. Refactor ```javascript contenteditable function htmlEncode(input) { return ($('_LT_div/_GT_').text(input).html()); } var saveFormat = function(txt) { $.each(arguments, function (i, item) { if (i _GT_ 0) { item = htmlEncode(item); txt = txt.replace("{" + (i - 1) + "}", item); } }); return txt; }; jasmine.getEnv().execute(); ``` Demo
## 5. Repeat ```javascript contenteditable function htmlEncode(input) { return ($('_LT_div/_GT_').text(input).html()); } var saveFormat = function() { var args = Array.prototype.slice.call(arguments); var txt = args.shift(); $.each(args, function (i, item) { item = htmlEncode(item); txt = txt.replace("{" + i + "}", item); }); return txt; }; jasmine.getEnv().execute(); ``` Demo
## Testing HTML   Jasmine is DOM agnostic comes without tools to set up HTML fixtures

Definition: A test fixture is a fixed state of a set of objects used as a baseline for running tests.
## First Solution in memory fixture with jQuery ```javascript contenteditable describe('trivial jQuery plugin', function () { var fixture; beforeEach(function () { fixture = $('_LT_div_GT_some HTML code here_LT_/div_GT_'); }); it('should do something', function () { fixture.myPlugin(); expect(fixture).toHaveClass("newClass"); }); }); jasmine.getEnv().execute(); ``` ... only works for trivial plugins!
## Clumsy Solution directly append to/remove from DOM ```javascript contenteditable describe('my jQuery plugin', function () { beforeEach(function () { $('#fixture').remove(); $('body').append('_LT_div id=_QUOT_fixture_QUOT__GT_HTML_LT_/div_GT_'); }); it('should do something', function () { $('#fixture').myPlugin(); expect($('#fixture')).toHaveClass("newClass"); }); }); jasmine.getEnv().execute(); ```
## jasmine-jquery [custom matchers](https://github.com/velesin/jasmine-jquery#jquery-matchers), HTML/style/JSON fixtures, event spies ```javascript contenteditable describe('my jQuery plugin', function () { beforeEach(function() { jasmine.getFixtures().fixturesPath='js/5_jasmine-demo_jquery'; jasmine.getFixtures().load('jquery.myPlugin.spec.html'); }); it('should do something', function() { var $div = $('#helloWorld').myPlugin(); expect($div).toHaveClass("newClass"); }); }); jasmine.getEnv().execute(); ``` Demo
## TDD → BDD
## Spies test behaviour ```javascript contenteditable small describe('Garfield', function () { describe('when told to be nice', function() { var garfield; beforeEach(function() { garfield = new Garfield(); spyOn(garfield, 'goToFridgeAndEatPizza').andCallThrough(); }); it('should answer with ok', function() { var answer = garfield.beNice(); expect(answer).toEqual("ok"); }); it('should steal pizza', function () { garfield.beNice(); expect(garfield.goToFridgeAndEatPizza).toHaveBeenCalled(); }); }); }); jasmine.getEnv().execute(); ``` A spy can stub any function and tracks calls to it and all arguments. Demo
# Danke!

Created by Johannes Hoppe | Print PDF | GitHub