· 9 years ago · Jan 25, 2017, 11:48 AM
1# Introduction to Javascript (and Jasmine)
2
3Javascript week is a very special time at Makers. For me it was the first time I wanted to throw things at the projector and scream ‘HERESY’. It was not my beloved Ruby, and I was not happy about it.
4
5I felt strangely codeblind - unable to make sense of all the curly brackets, parenthesis, semicolons and the damn word ‘function’ here there and everywhere. My reaction was to fill my soul with a deep and passionate hatred of Javascript as a language, and anyone involved with it was the Devil. I was wrong.
6
7Despite my incredible prejudice against Javascript I was unable to deny the cool things this ugly little duckling could do in a browser. It made my webpages shimmer and sparkle- and I knew I had to get over this.
8
9The first step to recovery, was to implement our beloved friend Fizzbuzz in this [confusingly-named new language](http://www.quora.com/Why-is-JavaScript-called-Java) - although we are going to call it 'Javabuzz' in homage to our new friend Javascript. You should be familiar with FizzBuzz from your introduction to RSpec with Ruby. If that isn't the case, now might be a good time to find an adult.
10
11## Testing with Jasmine
12
13Jasmine is a testing framework built by the wonderful people over at [Pivotal labs](http://pivotallabs.com/). To get started, go to the [latest build page for Jasmine](https://github.com/pivotal/jasmine/releases) and download the most recent *standalone* version in a zip file. The version I will be using in this tutorial is [v2.0.2](https://github.com/pivotal/jasmine/releases/download/v2.0.2/jasmine-standalone-2.0.2.zip).
14
15Once you have downloaded the .zip archive and unpacked it, open the 'SpecRunner.html' file in your browser as a sanity check. It should look like this:
16
17
18
19This runs a suite of Jasmine tests on a demo Song project. Feel free to inspect the files in the spec and src directories to see what's going on, but it might make a lot more sense if we work through creating our own project from scratch.
20
21To use Jasmine in your own project create a new directory with the appropriate name, e.g. JavaBuzz and transfer over the lib directory intact, e.g. from your project dir
22
23```sh
24cp -r ~/Downloads/jasmine-standalone-2.0.2/lib .
25```
26
27Your file structure within your project directory should now look like this:
28
29```sh
30→ tree
31.
32└── lib
33 └── jasmine-2.0.2
34 ├── boot.js
35 ├── console.js
36 ├── jasmine-html.js
37 ├── jasmine.css
38 ├── jasmine.js
39 └── jasmine_favicon.png
40
41```
42
43Now we need to create a test file, and a file for our code. Create a spec and a src directory and then let's touch a couple of files!
44
45* spec/JavabuzzSpec.js
46* src/Javabuzz.js
47
48There's one last step before we can make sure our Jasmine setup is complete: copy over the SpecRunner.html to the root of our project directory. SpecRunner.html includes the following code
49
50```html
51 <!-- include source files here... -->
52 <script type="text/javascript" src="src/Player.js"></script>
53 <script type="text/javascript" src="src/Song.js"></script>
54
55 <!-- include spec files here... -->
56 <script type="text/javascript" src="spec/SpecHelper.js"></script>
57 <script type="text/javascript" src="spec/PlayerSpec.js"></script>
58```
59
60Which needs to be changed over to the following:
61
62```html
63 <!-- include source files here... -->
64 <script type="text/javascript" src="src/Javabuzz.js"></script>
65
66 <!-- include spec files here... -->
67 <script type="text/javascript" src="spec/JavabuzzSpec.js"></script>
68
69```
70
71Our directory structure now looks like this:
72
73```sh
74→ tree
75.
76├── SpecRunner.html
77├── lib
78│ └── jasmine-2.0.2
79│ ├── boot.js
80│ ├── console.js
81│ ├── jasmine-html.js
82│ ├── jasmine.css
83│ ├── jasmine.js
84│ └── jasmine_favicon.png
85├── spec
86│ └── JavabuzzSpec.js
87└── src
88 └── Javabuzz.js
89
90```
91
92Now we need to start writing our tests. YAY TESTING! <3
93
94The first thing to remember with Jasmine, is it lacks the 'context' method that RSpec provides for Ruby. But don't cry little ones - we can simply reuse the 'describe' method. At this point, we're going to zoom in to the creation of the describe method. Why? Because it's an excellent way to get used to building methods in Javascript - and it's also how you start a Jasmine test file :zap:
95
96## The first rule of Javascript is...
97
98**Close your brackets the _moment_ you open them.** No ifs, no buts. This is non-negotiable. Personally, I like to have auto-complete and auto-matching disabled in my editor (Sublime Text) so I have complete control over what appears in my code.
99
100That aside, the one practice that turned Javascript from a chore to a joy for me was this one little habit. Open bracket, close it.
101
102Why is this so important? Javascript has a lot of nesting, and with all those brackets, curly braces and semicolons flying around things can get confusing and quickly.
103
104Just in case there is any confusion here, I am going to show a step-by-step example of how I would write a describe method in Javascript/Jasmine:
105
106#### Step 1
107Type the name of your method. In our case:
108```javascript
109describe
110```
111
112#### Step 2
113Open and close parenthesis, and end the line with a semicolon. This creates the wrapper for your method:
114
115```javascript
116describe();
117```
118#### Step 3
119Next, inside the parenthesis, open and close a pair of quote marks.
120
121```javascript
122describe('');
123```
124
125#### Step 4
126Once you have your quote marks, put a comma afterwards to separate your arguments from the next part of the method definition:
127```javascript
128describe('',);
129```
130#### Step 5
131Now we need to declare the body of our method. In Ruby, we would type 'do' and 'end' - but in javascript we use the dreaded 'function' keyword.
132
133We are going to step outside of our describe method right now, and focus on 'function' as its own entity. The reason we will do this will become apparent in a short while.
134
135##### Step 5a
136```javascript
137function
138```
139##### Step 5b
140```javascript
141function()
142```
143##### Step 5c
144```javascript
145function(){}
146```
147##### Step 5d
148The space is just a good practice/convention:
149```javascript
150function() {}
151```
152
153Painful isn't it? I realise that this is laborious and a bit prescriptive - but this is honestly the best way to build a habit that will make your Javascript life so much more more comfortable.
154
155Now to integrate our function syntax into our describe method.
156
157#### Step 6
158After that argument-separating comma, put your function syntax in, just how we learnt in step 5:
159
160```javascript
161describe('', function() {});
162```
163Then place your cursor in-between the curly braces, and press return:
164
165```javascript
166describe('', function() {
167//method statements go here
168});
169```
170
171If you can drill this and make it habit when you're typing your code, you will have less syntax errors, and it should also help your eyes navigate Javascript, and start to see the segments more easily.
172
173## An aside on function(){}
174
175Function has multiple uses in Javascript - and since we are Rubyists first and foremost, let's consider that this one keyword can be used to create versions of all the following in Ruby:
176
177#### Ruby 'class' creation of object factories becomes:
178```javascript
179var Classname = function () {};
180```
181_Some debate exists on the best way to declare function this way, but this is the best practice according to [John Resig ...](http://ejohn.org/blog/javascript-as-a-first-language/)_ Note also that JavaScript does not have 'classes' in the same way as Ruby, however for the moment you can think of the functions as being able to act as 'object factories' in the same way that classes are 'object factories' in Ruby.
182
183#### Ruby 'do ... end' blocks becomes:
184```javascript
185methodName(function() {
186// codeblock goes here
187});
188```
189
190#### Ruby 'def' definition of methods becomes:
191```javascript
192Classname.prototype.methodName(function() {
193// codeblock goes here
194});
195```
196
197When you consider that, is it any wonder that Javascript code seems littered with the word 'function'? If you would like more background reading, now would be an excellent time to peruse the [js_functions pill](https://github.com/makersacademy/course/blob/master/pills/js_functions.md) in the Makers Course repository.
198
199### Back to Javabuzz
200
201So, we have our JavabuzzSpec.js file, looking all empty and yearning for meaning. Let's give it some purpose! Pop a describe method in there, and let's give it some context in-between those dang quote marks:
202
203```javascript
204describe('Javabuzz', function() {
205
206});
207```
208
209#### Variable time
210
211Now let's declare us a nice variable just inside that function block. Remember how function is used in place of Ruby's do/end?
212
213```javascript
214describe('Javabuzz', function() {
215
216 var javabuzz;
217
218});
219```
220
221Oooh, look - a new keyword! What does `var` do when it's at home? Well there's a really good explanation over on [Stack Overflow](http://stackoverflow.com/questions/1470488/what-is-the-function-of-the-var-keyword-and-when-to-use-it-or-omit-it) but let me summarise here in case you don't feel like leaving us right now.
222
223Basically it's an issue of scope and clarity. `var` means that within a function, you are declaring a **local variable.** This means that in the example above, `var javabuzz` is available only between the nearest `{}`.
224
225What happens if you don't specify `var`? Basically Javascript looks up the scope chain - that is to say, it looks in every parent function, until it finds the variable (in this case `javabuzz`). Once it hits the uppermost level, if it still can't find `javabuzz` it will create it for you - and because it has been created at the top level, it becomes a global variable, available to every function in the program, which is generally considered a 'bad thing'. It's bad because if other JavaScript libraries use the same name in the global variable space, your program will break, so use `var`!
226
227#### Contextual healing
228
229As I mentioned previously, Jasmine has no equivalent for Rspec's `context` block. While this is both sad and upsetting, it's by no means going to stop us writing meaningful tests. We can just nest another `describe` function in the place of context:
230
231```javascript
232describe('Javabuzz', function() {
233
234 var javabuzz;
235
236 describe('knows when a number is', function() {
237
238 });
239
240});
241
242```
243
244However, it does support `it` - so let's put one of those in!
245
246```javascript
247describe('Javabuzz', function() {
248
249 var javabuzz;
250
251 describe('knows when a number is', function() {
252
253 it('divisible by 3', function() {
254
255 });
256
257 });
258
259});
260```
261
262Well alright, this is looking pretty good - don't you think? I'm excited. However, now we need to actually have an instance of Javabuzz to test against, so let's go ahead and create that in our `it` block:
263
264```javascript
265describe('Javabuzz', function() {
266
267 var javabuzz;
268
269 describe('knows when a number is', function() {
270
271 it('divisible by 3', function() {
272 javabuzz = new Javabuzz();
273 });
274
275 });
276
277});
278```
279
280Note that when we refer to `Javabuzz` when we are instantiating our version of the class - we MUST add the `();` at the end, like so:
281
282```javascript
283javabuzz = new Javabuzz();
284```
285
286This is non-negotiable and Javascript WILL be pedantic about it, so please for the sake of all that is good and holy, whenever referring to a class or method, put the bloody `();` after the name, or we'll never hear the end of it. (The most notable exception for this rule is when we are declaring a class method using the `prototype` syntax - which we'll cover shortly.
287
288So close! All we need now is an expectation within our `it` block and we are an unstoppable testing machine:
289
290```javascript
291describe('Javabuzz', function() {
292
293 var javabuzz;
294
295 describe('knows when a number is', function() {
296
297 it('divisible by 3', function() {
298 javabuzz = new Javabuzz();
299 expect(isDivisibleByThree(3)).toBe(true);
300 });
301
302 });
303
304});
305```
306#### Javascript Convention Centre
307
308Now, being children of Makers, you should all be familiar with Fizzbuzz as a concept so I won't waste time re-explaining the wheel. However, it's worth mentioning a couple of points about the naming of our 'divisible by three' method.
309
310Firstly you'll notice that instead of separating words using underscores a la Ruby, we are omitting spaces, and using uppercase letters to enforce the separation between words. **thisIsCalledCamelCase,** and it is a Javascript convention.
311
312Secondly, you'll see that there is no `?` at the end of the method - which is something that we were able to do with Ruby. Unfortunately Javascript doesn't allow us this as a syntactic option, so to get around that Javascript developers have come up with _yet another_ convention: to put `is` at the beginning of methods which would usually end in `?` in Ruby. Got it? Great!
313
314### Javascript Convention Centre
315
316Now, if you head to your terminal application, and whilst in the home directory of your app type `open SpecRunner.html`, your default browser should open with the results of your very first (failing) Jasmine test. Ooooooooh! It'll be all red and angry, and should have the following error:
317
318```bash
319Javabuzz knows when a number is divisible by 3
320ReferenceError: Javabuzz is not defined
321```
322
323This is Jasmine's very dramatic way of saying that it doesn't know what we mean when we refer to `Javabuzz();` on line 8(ish) of our spec file. Well you and I both know we meant the Javabuzz class, so let's head on over to **src/Javabuzz.js** and create the class now:
324
325```javascript
326var Javabuzz = function(){};
327```
328
329Sweet, error message has been changed!
330
331```bash
332ReferenceError: isDivisibleByThree is not defined
333```
334### The Ties That Bind
335
336Now we need to define the `isDivisibleByThree` method. Fun times. In Ruby, if we wanted a method to be bound to a class we would simply declare that method inside the class.
337
338However, Javascript does it differently (spotting a pattern here?) This is where `prototype` comes in!
339
340#### Prototype
341
342If you are using Sublime Text, you should have a lazy little shortcut built in to the editor. Type `proto`, hit 'tab' and the following code should be generated for you:
343
344```javascript
345class_name.prototype.method_name = function(first_argument) {
346 // body...
347};
348```
349
350Whilst I would encourage you to learn this syntax and **not** rely on editor shortcuts, this does give us a nice little model for creating a class method in Javascript.
351
352So we want to swap out `class_name` for the class we want to bind the method to, `method_name` becomes the name of the method and `first_argument` is pretty self-explanatory :p So we should end up with this:
353
354```javascript
355var Javabuzz = function () {};
356
357Javabuzz.prototype.isDivisibleByThree = function(number) {
358 return true;
359};
360```
361
362(I have hardcoded `return true` in the method for the sake of speeding this along, hopefully that makes sense.)
363
364However, our test, is still failing, despite everything in our Fizzbuzz career telling us this should be enough to make that light go green. WTF???
365
366Well, the problem lies in our spec file. Take a look at that expectation again:
367
368```javascript
369expect(isDivisibleByThree(3)).toBe(true);
370```
371
372Javascript needs us to be more specific. It needs to know exactly what `isDivisibleByThree(3)` refers to. So let's help it out. Since 'isDivisibleByThree' is a method of our javabuzz instance, let's do some Ruby-style dot notation:
373
374```javascript
375expect(javabuzz.isDivisibleByThree(3)).toBe(true);
376```
377
378Now there can be no doubt that we mean **this particular method** belongs to **that particular class!**
379
380Refresh your browser, and SpecRunner.html should be a very happy Bunny. But all y'all Fizzbuzzers out there should know that a hard-coded `true` just isn't going to cut the mustard. We need something a bit more robust. Time for a counter test! Remembering that we have no `context`, set up another `describe` method on the same level as the one with the label 'knows when a number is', and do as your training has taught you:
381
382```javascript
383describe('knows when a number is NOT', function() {
384
385 it('divisible by 3', function() {
386 javabuzz = new Javabuzz();
387 expect(javabuzz.isDivisibleByThree(1)).toBe(false);
388 });
389
390});
391```
392
393This second test should be upsetting the balance of your greenery, and rightly so. Shame on you for hardcoding a value just to pass a test (just kidding!) However, we do need to fix that pronto. To our **Javabuzz.js** file!
394
395```javascript
396Javabuzz.prototype.isDivisibleByThree = function(number) {
397 return (number % 3 === 0);
398};
399```
400
401And with that, all your tests should be passing. Glorious, isn't it? This shouldn't be too surprising given your black belt in the Ruby version of Fizzbuzz, but I will take a moment to point out a couple of things here. Firstly, the parenthesis around our logic isn't strictly necessary. But it does make the statement look neater, so I'm keeping it in.
402
403The second thing to be aware of, is the `return` keyword. That one's a deal-breaker and it's essential that we use it - notice how much more specific we need to be with Javascript over Ruby?
404
405Given that by now, we all know our Fizz from our Buzz, we'll leave it up to you to create the rest of the tests. By the time you are finished the spec file should have 6 tests (2 tests for each method to make sure a number IS divisible by n, and NOT divisible by n).
406
407You may find you have a lot of repetition in the spec file - namely the instantiation of our Javabuzz class in each `it` statement. Remember the `let` syntax in Ruby? Jasmine affords us the same luxury! Just after we declare `var javabuzz` at the top level of our spec file, add the following:
408
409```javascript
410 beforeEach(function() {
411 javabuzz = new Javabuzz();
412 });
413```
414
415Now you can get rid of each line containing `javabuzz = new Javabuzz();`. Joy unparalleled.
416
417Since we're in a refactory kind of mood, let's see if we can make our code more elegant. The following should make sense given our previous Rubybuzz pedigree:
418
419```javascript
420Javabuzz.prototype._isDivisibleBy = function(number, divisor) {
421 return (number % divisor === 0);
422};
423```
424
425#### Private Dancer
426
427Notice that I have added an underscore to my method name? This is how we communicate to other Javascript developers that the following method is considered a private method - one that should only be available within the class.
428
429Now let's implement it (in one method first, to make sure everything is hunky dory:
430
431```javascript
432Javabuzz.prototype.isDivisibleByFifteen = function(number) {
433 return _isDivisibleBy(number, 15);
434};
435```
436
437Oh no! Our tests fail! Does anyone know why? BECAUSE WE ARE NOT BEING SPECIFIC ENOUGH!!! Javascript really does need you to go the extra mile. I know, I know - but it is worth it.
438
439#### this.
440
441We need to tell Javascript that `_isDivisibleBy();` belongs to the Javabuzz class. Now, since we are inside a method definition that is already bound to that class, all we have to do is add `this.` to the beginning of our method, and all our problems go away:
442
443```javascript
444Javabuzz.prototype.isDivisibleByFifteen = function(number) {
445 return this._isDivisibleBy(number, 15);
446};
447```
448
449Let's adjust the rest of our methods accordingly, and move on. Now, we have all of the numerical operations we could need to play Javabuzz, so now let's create our game method. First, as always, a wee test:
450
451```javascript
452describe('when playing, says', function() {
453
454 it('"Java" when a number is divisible by 3', function() {
455 expect(javabuzz.says(3)).toEqual("Java");
456 });
457
458});
459```
460
461Dang, we're back in the red. How do we fix it? Oh yeah, you know what time it is - PROTO TIME!
462
463```javascript
464Javabuzz.prototype.says = function(number) {
465 if (this.isDivisibleByThree(number)) {
466 return "Java";
467 }
468};
469```
470
471There shouldn't be too many surprises here at this point. Things to note:
472* `if` statement conditions need to be wrapped in parenthesis
473* `says` method name - just makes sense to me. Feel free to use whatever name works for you.
474
475Now this works like a charm for me, so I'm going to write the rest of my tests, to make sure I get "Java" for 3, "Buzz" for 5 and "Javabuzz" for 15, as well as returning the original number if it is not divisible by 3, 5 or both.
476
477Almost there - all we need to do now, is a straight conversion of what worked for us in Ruby. We can repeat the `if` statements, as long as we remember to lead with the `isDivisibleByFifteen` method - and not forgetting to `return number` if all else fails:
478
479```javascript
480Javabuzz.prototype.says = function(number) {
481 if (this.isDivisibleByFifteen(number)) {
482 return "Javabuzz";
483 }
484 if (this.isDivisibleByThree(number)) {
485 return "Java";
486 }
487 if (this.isDivisibleByFive(number)) {
488 return "Buzz";
489 }
490 return number;
491};
492```
493
494Wheee! All our tests pass. Rejoice! No, seriously, go celebrate. Do something nice for yourself. If you want to test your game, go to your browser, and in the same window where your SpecRunner.html is loaded up, open your console (Google Chrome shortcut is cmd + option + i) and at the prompt intitialise an instance of Javabuzz(); as you did in your spec file:
495
496```javascript
497var javabuzz = new Javabuzz();
498```
499
500and then throw some numbers at it:
501
502```javascript
503> javabuzz.says(3)
504<- "Java"
505
506> javabuzz.says(5)
507<- "Buzz"
508
509> javabuzz.says(15)
510<- "Javabuzz"
511
512> javabuzz.says(1)
513<- 1
514```
515
516Here endeth the lesson. Hope you had as much fun as I did - enjoy Javascript and go make something cool! <3
517
518### Related Pills
519
520* [js_functions pill](js_functions.md)
521* [js_arrays pill](js_arrays.md)
522* [js_conventions pill](js_conventions.md)
523* [JQuery events pill](jquery_events.md)
524
525### External Resources
526
527In addition to the 5 JavaScript and JQuery pills we have supplied for you in the course materials, you will also find the following external resources helpful:
528
529* [Codecademy - Javascript Track](http://www.codecademy.com/tracks/javascript) - They also have a JQuery track that is worth working through.
530* [JavaScript for Cats](http://jsforcats.com/) - An introduction for new Programmers.
531* [Awesome JavaScript](https://github.com/sorrycc/awesome-javascript) - An Awesome repository of JavaScript resources.