JavaScript

Aus Peter Fuerholz' Wiki
Zur Navigation springen Zur Suche springen

JavaScripts is the de facto standard for programming web applications. In my opinion it is even becoming the standard for UI programming at all and spreading out for the server as well. From this point of view it takes over one of the Java mantras: "write once, run everywhere". Since there exist many compilers translating other programming languages into JavaScript it is becoming "assembly language of the Web".

Reference

Variants

EcmaScript 262 (ES3)
  • published in 1999
  • New: regex, try-catch etc.
ES4
specification was never completed (abandoned)
EcmaScript 5 (ES5)
  • released in 2009
  • New: Object.create(), strict mode, bind() etc.
EcmaScript 6 (ECMAScript 2015 / ES6 / ES6 Harmony)
  • released in June 2015
  • New: Classes, Modules, arrow functions, promises etc.
EcmaScript 7 (ES6)
  • work in progress
  • New: concurrency and atomics, syntactic integration with promises, observable streams etc.

Things you need to know

CommonJS
This project was founded with the goal of specifying a common standard library mainly for JavaScript development outside the browser.

JavaScript syntax

  • Functions, Classes etc. are objects!
  • Each object has a reference to a prototype and to a this-object:
  • prototype is Object if not set otherwise
  • this is the browser's window if not set otherwise
  • A class (=Object) consists of a key-value map. The key is always a String. Accessing it can happen on two ways: obj.<key> and obj[key]
  • Literals:
  • Object: var obj1 = {};
  • Array: var arr = ["a", "b", "c"];
  • String: var str = "abc"; // or 'abc'
for JSON objects "" must be used
  • Number: var nb = 1;
number is comparable to Java double. Use parseInt / parseFloat / toFixed to distinguish.
  • Defining a variable without var creates a global variable!
  • Variable scopes can be defined by functions only, {} does not help within a code block (in comparison to Java).
  • A variable (or function et al) can be null or undefined (if unset / undefined)!
  • Everything evaluates to true except: false, undefined, null, 0, NaN, ""
  • function parameters are optional by default. You can access all parameters via arguments (type: Array)
ES6: Cannot be used within thick-arrow-functions (=> ()) !!
  • No method overloading:
 foo() {...}
 foo(param1) {...}
 foo(param1, param2) {...}
leads to only one function, the one defined last!
  • Functions on Array:
  • forEach(<function>)
  • filter(<function>)
  • map
  • reduce
  • Object:
  • methods: toString
  • static methods: getPrototypeOf (Don't try to access the prototype via obj.prototype, it returns undefined!)
  • Array is a first class citizen in JavaScript and is mutable what means that is more similar to Java's List than Array. Good introduction to its functions: 8 lessons on Egghead.io

Classes

this in JavaScript vs in other OO languages

You’ll notice that we are using this to represent the object that was passed into the function. But, using this in JavaScript is not the same as using this in other languages such as C# or Java. In those languages, this is "the object that I am." In JavaScript, this is "the context I was called from."

see this post

Class example

 function Person(gender, name) {
   this.gender = gender;
   this.name = name;
 }
 
 Person.prototype.getName = function() {
   return (this.gender ? "Mr " : "Mrs ") + this.name;
 }
 
 
 function _extends(_sub, _super) {
   var __ = function() {};
   __.prototype = _super.prototype;
   _sub.prototype = new __();
   _sub.prototype.constructor = _sub;
 }
 
 
 function Male(name) {
   Person.call(this, true, name);
 
   // private method:
   Male.prototype.getAge = function() {
       return 22;
   }
 }
 _extends(Male, Person);
 
 var male = new Male("Mischa Scheurer");
 
 console.log(male.getName());          // Mr Mischa Scheurer
 console.log(male instanceof Male);    // true
 console.log(male instanceof Person);  // true
 
 console.log(male.getAge());           // 22
 
 
 // this is a real overwrite!
 Male.prototype.getName = function() {
   return "Dear " + Person.prototype.getName.call(this);
 }
 
 
 console.log(male.getName());          // Dear Mr Mischa Scheurer
 console.log(male instanceof Male);    // true
 console.log(male instanceof Person);  // true

Function methods

A function is an object of its own and provides following methods:

()
Executes the function; if it is a method call the value on the left is used as this, otherwise 'window'
call(<obj>)
Executes function with obj as 'this'
apply(<obj>)
Executes function with obj as 'this'. Difference to call ist that obj shall be an array (in case of multiple parameters)
Example:
 function fullName(title, lastName) { return title + this.name + this.lastName; }
 var oma = { name : "Oma" };
 fullName.call(oma, "Frau", "Torbogen");        // -> "Frau Oma Torbogen"
 fullName.apply(oma, [ "Frau", "Torbogen" ]);   // -> "Frau Oma Torbogen"
bind(<obj>)
Binds obj to this and returns this in a new object.
Example:
 var bound1 = fullName.bind(oma);
 bound1("Frau", "Torbogen");  // -> "Frau Oma Torbogen"
 var bound2 = fullName.bind(oma, "Frau", "Torbogen");
 bound2();  // -> "Frau Oma Torbogen"


Modules

Spring understanding contains a good overview about the different module systems in JavaScript. In difference to this article I noticed that node.js packages (an extension to CommonJS) is spreading into the client and we see such modules in client code more-and-more. (When using React you use npm anyway for importing different packages...) A comparision between CommonJS, RequireJS and AMD can be found here. (And here is a multipart video tutorial about RequireJS.)

CommonJS

Here are some typical use cases for importing:

 var React = require('react');
 var PropTypes = React.PropTypes;
 var Square = require('./Square');
 var canMoveKnight = require('./Game').canMoveKnight;

And here for exporting:

 module.exports = Square;
 exports.moveKnight = moveKnight;

But don't do:

 exports = Square;

This would make exports point to Square, but module.exports stays untouched! See here ("simplified way to view a JS file in Node")

Node.js

Node.js started out to take over JavaScript to the server side. Today Node.js stands for a package manager, 100'000 of modules, a runtime system, a JavaScript library, command line tools etc.

Tools:

npm
see next chapter
node
JavaScript runtime. Start your script with: node <filename.js>

npm

The Node.js tool npm can work as build system / package manager. Typical workflow:

  1. Create / copy package.json file
  2. Run npm install
  3. (Depending on package.json content:) execute npm start (Typically used in React.JS where Babel (et al) gets instructed to regenerate build.js on each update of a Javascript file.

If you start using require in your browser you are using npm what means that you are forced to use a build tool! ("All package managers need build tools.") Typically you have following code in package.json for this:

   "scripts": {
     "start": "watchify index.js -v -t babelify -o bundle.js"
   }

Important Commands

  • see npm version: npm -v
  • update local packages: npm update
  • check for outdated global packages (should not return a result): npm outdated -g --depth=0
  • update global packages: sudo npm update -g

Important Bundles

There exist more than 200'000 packages available on npm. Here are some explanations for important ones:

Browserify
Allows to import packages by using the 'require' function what means the same way we know from the server side. As such Browserify is bringing npm module world to the browser. That means:
  • you can make use of all the existing node.js packages
  • you have leveraged modules to the browser (you can use 'require' / 'export')
  • Furthermore it enables to bind Javascript files into one bundle (file) together.
JavaScript code handled with this tool cannot use the locale filesystem! This code has to be run in the browser and thus cannot use the local fs.
It consists of:
  • a command line tool browserify (e.g. for binding Javascript files into one bundle (file) together)
  • JS functions 'require' (et al)
Watchify
Same as Browserify but contains a 'watch mode'. That means you don't have to invoke manually Browserify after each code change.
Bower
A client side only package manager, comparable to npm + Browserify. Differences (see here):
  • Bower can files like css bind together as well.
  • With npm + Browserify you get Javascript modules out-of-the-box.

ES6

Functions

See here for a thorough explanation of the arrow function definition (=>).


Some examples ES5 vs ES6:

Import:

ES5:

 var createStore = Redux.createStore;

ES5 with npm:

 var createStore = require('redux').createStore;

ES6:

 const { createStore } from Redux;

ES6 with npm:

 import { createStore } from 'redux';

Function:

ES5:

 function counter(state, action) {
   if (typeof state === 'undefined') {
     return 0;
   }
   ...
 }

ES6 with default arguments:

 const counter = (state = 0, action) => {
   ...
 }

Object literal property value shorthand

There are cases where an object consists of key = value. Instead of writing

 function createMonster(name, power) {
   return { type: 'Monster', name: name, power: power };
 }

you can use the 'Object literal property value shorthand':

 function createMonster(name, power) {
   return { type: 'Monster', name, power };
 }

See here.

Side effect free list operations:

Append (append() is mutating!): ES5:

 return list.concat(0);

ES6:

 return [...list, 0];

Remove (splice() is mutating!): ES5:

 return list.slice(0, index).concat(list.slice(index + 1));

ES6:

 return [
   ...list.slice(0, index),
   ...list.slice(index + 1)
 ];

Update one array element (increment its number value here): ES5:

 return list.slice(0, index).concat(list[index] + 1).cohttps://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functionsncat(list.slice(index + 1));

ES6:

 return [
   ...list.slice(0, index),
   list[index] + 1,
   ...list.slice(index + 1)
 ];

Object cloning:

If you need to create a new Object based on another one and update some values on it you have following ES6 possibilities ES6: (-> Polyfill needed, see here):

 Object.assign({}, toClone, {
   prop1 : attr1,
   prop2 : attr2 
 }

Assigns all properties of toClone on 1st parameter ({}), then applies the prop1 and prop2 on it (arbitrary possible). Last update wins! ES5: (supported by Babel, stage 2, proposed for ES7 -> .babelrc must contain "presets": ["stage-2"] and have installed: npm install babel-preset-stage-2)

 {...toClone, prop1 : attr1, prop2 : attr2 };


Promises and Generators

Promises and Generators allow to implement asynchronous functions in a very advanced style. Best example is this video. Here some explanations:

Promise
A promise is something similar as a Future in Java although it is not multi-threaded! A promise has following API. Typically you call done on a promise passing over two functions: onFulfilled and onRejected.
A promise can have following states: pending, fulfilled, rejected. There exist different implementations. One of them is the one from Forbes Lindesay. This is the guy behind the video above.
Generator
Generator functions have two outstanding characteristics:
  • function* <function name>()
  • yield keyword
The return value of a generator function is a generator object. This object implements the Iterator/Iterable interface (e.g. next())
Links: Generator function, Generator class, Iteration protocol
To understand Generator functions you have to know the following:
  • The method is run until the first yield keyword is encountered and the value thereafter is returned. On second call execution resumes after last yield encounter and runs until next yield where that value is returned.
  • Instead of calling the Generator function often times you are calling next() on the generator's return value. next() allows to pass over a value which gets returned next. This is fundamental when using Generators together with Promises! With next(<value>) you inject the result of the Promise. Additionally you can call throw() so that the generator function throws an exception.
  • See TestProject1/yieldTest.js (runs in WebStorm only)

Helpful modules

Expect
Assertion library
Deep-Freeze
Library to check that an object does not get mutated.
ClassNames
Concatenates CSS classnames (strings) together depending on a boolean condition.
Externalify
Tool for Browserify... (?)
Jasmine
Unit testing library (concept identical to JUnit)

WebStorm

WebStorm is a good IDE for JavaScript etc. WebStorm has 'File Watchers' with whom you can configure to translate them on the fly. If translation does not occur the following helps: File, Invalidate Caches / Restart...