JavaScriptChaos

Weird Stuff

true
> [] + []
''
> [] + {}
'[object Object]'
> {} + []
0
> alert.call.call.call.call.call.apply(function (a) {return a}, [1,2])
2
> true === 1
false
> true + true === 2
> 9999999999999999
10000000000000000
> 0.1 + 0.2 === 0.3
false
> '2' + 1
'21'
> '2' - 1
1
> 0 === -0
true
1/0 === 1/-0
false

Beware the context

var x = {
 foo() {
   return this;
 },
};
var y = x.foo;

x.foo();
y();
function foo() {
  return this;
}

foo.call(console);
foo.call(undefined);
foo.bind(foo)();
var foo = {
  number: 1,
  foo() {
    return this.number;
  }
};
var bar = {
  number: 23;
};
var x = foo.foo;
foo.foo();
x();
x.call(bar);

ES2015 - new stuff!

Working in new Browsers/Node

Let & Const

var foo = 1;
{
    var foo = 2;
}
console.log(foo);
// 2
var foo = 1;
{
    let foo = 2;
}
console.log(foo);
// 1
var foo = 1; // this is const, do not change!

function evil() {
    foo = 2;
}
evil();
console.log(foo);
// 2
const foo = 1; // this is const, do not change!

function evil() {
    foo = 2;
}
evil(); // TypeError, Assignment to constant variable.
console.log(foo);
const foo = { a: 1 };
foo.a = 2;
console.log(foo.a);
// 2
var foo = 1;
var foo = 2;
let foo = 1;
const foo = 2; // Throws, foo already declared

Destructuring

var foo = {
  bar: 1,
  batz: 2
};
var bar = foo.bar;
const foo = {
  bar: 1,
  batz: 2,
};
const { bar } = foo;
var a = [1, 2, 3]
var b = a[0];
var c = a[2];
const a = [1, 2, 3];
const [b, , c] = a;
function foo(a) {
    if (a === undefined) {
        a = 1;
    }
}
var a = 1;
var b = 2;
// Swap
var c = a;
a = b;
b = c;
const a = 1;
const b = 2;
// Swap
[a, b] = [b, a]
function foo(a = 1) {
}
function foo(a = { a: 1, b: 2 }) {}
function bar({ a = 1, b = 2 }) {}
function bar({ a = 1, b = 2 } = {}) {}

Spread & Rest

function foo() {
    var a = arguments; // EVIL!
}
function foo(...a) {
}
function foo(a, b, ...c) {
}
var a = [1, 2, 3];
var b = [2, 4, 6];
var c = a.concat(b);
const a = [1, 2, 3];
const b = [2, 4, 6];
const c = [...a, ...b];
console.log.apply(console.log, ...[1, 2, 3]);
console.log.call(console.log, 1, 2, 3);
function foo(...a, b, ...c) { // NOPE
}

Arrow Functions

function() {
    return 1;
}
() => 1
function() {
    return { a: 1 };
}
function(a) {
    return a;
}
var self = this;
function() {
    return self.foo;
}
() => this.foo;
a => a
() => ({ a: 1 });

Template Literals

var x = 'foo\nbar';
const x = `foo
bar`;
var a = 'foo' + 1 + 'bar';
const a = `foo ${1} bar`;

Object Literals

var x = {
    foo: function() {},
    bar: bar,
};
x['batz'] = 123;
const x = {
    foo() {},
    bar,
    ['batz']: 123,
};

Classes

 

class Foo extends Bar {
    static foo = 123;        // ESNext (Stage 2)
    foo = 123;               // ESNext (Stage 2)
    constructor() {
        super();
    }
    static bar() {
    }
    bar() {
        super.bar();
    }
}

new Foo().bar();
Foo.foo;

Promises

function later () {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("yay"), 1500)
  })
}

function muchLater () {
  return later().then(later)
}

const p = later()

p
  .then(x => x.y.z)
  .catch(err => console.error(err))
  .then(muchLater)
  .then(x => console.log(x))
  .then(x => console.log(x))

const p2 = p.then(later)
p2.then(x => console.log(x))
p2.then(x => x.y.z)
  .catch(err => console.error(err))
  .then(x => fetch("http://google.com"))

Promises

Generators

function* generator() {
  yield 'f'
  yield 'o'
  yield 'o'
}
const foo = generator();
for (let bar of foo) {
    console.log(bar)
}
// 'f'
// 'o'
// 'o'
[...foo()]
// ['f', 'o', 'o']
function* genGenerator() {
    yield* 'foo';
}

[...genGenerator()];
// ['f', 'o', 'o']
function* generator() {
    yield 1;
    console.log(1);
    yield 2;
    console.log(2);
    yield 3;
    console.log(3);
}
function* generator() {
    yield 1;
    yield 2;
    return 3;
    yield 4;
}
// [...generator()]
// [1, 2]

Maps, Sets, Weak

  • Iterable
  • Key, Value Pairs
  • Key beliebig
  • Kein Iterable
  • Key, Value Pairs
  • Key muss Reference sein
  • Zählt nich als ref für GC!

Map

WeakMap

  • Iterable
  • Value List
  • Value beliebig
  • Kein Iterable
  • Key, Value Pairs
  • Value muss reference sein
  • Zählt nich als ref für GC!

Set

WeakSet

Symbols

const symbol = Symbol();
const descriptiveSymbol = Symbol('foo');
symbol !== Symbol();
descriptiveSymbol !== Symbol('foo');
const S1 = Symbol.for('foo');
const S2 = Symbol.for('foo');

S1 === S2
const foo = {
  [Symbol()]: 'foo',
  [Symbol('foo')]: 'bar',
  [Symbol.for('bar')]: 'baz',
  what: 'ever'
};
console.log(Object.keys(foo))
// <- ['what']

console.log(JSON.stringify(foo))
// <- {"what":"ever"}

for (let key in foo) {
  console.log(key)
  // <- 'what'
}

console.log(Object.getOwnPropertyNames(foo))
// <- ['what']

console.log(Object.getOwnPropertySymbols(foo))
// <- [Symbol(), Symbol('foo'), Symbol.for('bar')]

Ponyfoo

https://ponyfoo.com/articles/es6

Linting

Linting History

  • JSLint
  • JSHint
  • JSCS
  • ESLint

Config

module.exports = {
  'extends': 'marudor',
  'env': {
    'browser': true,
  },
  'globals': {
  },
  'rules': {
    'quote-props': 1,
    'no-console': 0,
    'no-unused-expressions': 1,
    'class-property/class-property-semicolon': 2,
    'generator-star-spacing': 0,
  },
  'plugins': [
    'class-property',
  ],
};

Transpiler

Auswahl

Babel Facebook
Closure Google
Traceur Google
Typescript Microsoft

Focus Babel

Transpiler - Babel

  • ES6 (Next) rein, ES5 raus
  • Plugin basiert
  • Ohne Plugin 0 Funktion
  • Polyfill (babel-polyfill)
  • JSX (React)
  • Flow support

Babel - Config

{
  "presets": ["es2015"]
}

Babel - Config

{
  "presets": [["env", {
    "targets": {
      "chrome": 43,
      "firefox": 42,
      "safari": 9,
      "opera": 30,
      "edge": 12,
      "iOS": 9
    },
    "useBuiltIns": true, "loose": true
    }], "stage-2", "react"
  ],
  "plugins": [
    "transform-decorators-legacy"
  ],
  "env": {
    "development": {
      "plugins": [
        "transform-react-jsx-source",
        "flow-react-proptypes",
        "transform-dev-warning"
      ]
    },
    "production": {
      "plugins": [
        "transform-react-constant-elements"
      ]
    }
  }
}

Beispiel

class Foo extends Bar {
    static foo = 123;        // ESNext
    foo = 123;               // ESNext
    constructor() {
        super();
    }
    static bar() {
    }
    bar() {
        super.bar();
    }
}

new Foo().bar();
Foo.foo;

Beispiel

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var Foo = function (_Bar) {
    _inherits(Foo, _Bar);
    // ESNext
    function Foo() {
        _classCallCheck(this, Foo);

        var _this = _possibleConstructorReturn(this, _Bar.call(this));

        _this.foo = 123;
        return _this;
    } // ESNext

    Foo.bar = function bar() {};

    Foo.prototype.bar = function bar() {
        _Bar.prototype.bar.call(this);
    };

    return Foo;
}(Bar);

Foo.foo = 123;


new Foo().bar();
Foo.foo;

Resources

  • http://babeljs.io/
  • https://github.com/google/traceur-compiler
  • https://developers.google.com/closure/

Modules

Modul Standards

  • Globals
  • AMD (Asynchronous module definition)
  • UMD (Universal module definition)
  • CommonJS (Node)
  • ES2015
  • Globals
  • AMD (Asynchronous module definition)
  • UMD (Universal module definition)
var a = require('./a');
a.foo();

var b = require('./b');
b();
import { a } from './a';
import andererName from './b';

a();
andererName();
module.exports = {
    a: function() {
        return 'Ich bin a';
    }
};
module.exports = function() {
    return 'Ich bin b';
}

a.js

b.js

export function a() {
    return 'Ich bin a';
}
export default function() {
    return 'Ich bin b';
}

Funktioniert so nirgends

 

Standard existiert, Node bleibt bei CommonJS (vorerst)

Tooling to the rescue!

Browser -> Bundler

Node -> Babel

Bundler

  • Browserify
  • Webpack
  • Rollup

Resources

  • http://browserify.org/
  • https://webpack.github.io/
  • http://rollupjs.org/

ESNext

Object Rest & Spread (Stage 3)

var foo = { a: 1, b: 2, c: 3 };
var a = foo.a;
var r = { b: foo.b, c: foo.c };
const foo = { a: 1, b: 2, c: 3 };
const { a, ...r } = foo;

Async / Await (ES2017)

function a() {
    return Promise.resolve()
    .then(() => 1);
}
async function a() {
    await Promise.resolve();
    return 1;
}
function b() {
    return a()
    .then(n => n + 1);
}
async function b() {
    const n = await a();
    return n + 1;
}
let a = (() => {
    var ref = _asyncToGenerator(function* () {
        yield Promise.resolve();
        return 1;
    });

    return function a() {
        return ref.apply(this, arguments);
    };
})();

let b = (() => {
    var ref = _asyncToGenerator(function* () {
        const n = yield a();
        return n + 1;
    });

    return function b() {
        return ref.apply(this, arguments);
    };
})();

Decorator (Stage 2)

 

class Cat {
    @readonly
    meow() {
        return 'meow';
    }
}
const dog = new Cat();
dog.meow = () => 'wuff';    // Exception: readonly
function readonly(target, key, descriptor) {
    descriptor.writable = false;
    return descriptor;
}
@Cat / @Cat('mau')
class Animal {
}
const cat = new Animal();
cat.meow();
// meow
function Cat(target) {
    return class Cat extends target {
        meow() {
            console.log('meow');
        }
    }
}
function Cat(fnName) {
    return function(target) {
        return class Cat extends target {
           [fnName]() {
                console.log('meow');
            }
        }
    }
}

Typen?

Typen!

Typescript

  • Microsoft (& Google)
  • Eigene Sprache
  • Superset
  • Nicht neuste ES features
  • Open Source

Flow

  • Facebook
  • CLI Tool für JS
  • "Lernt" typen durch nutzung
  • Open Source

function square(a) {
	return a * a;
}

square(3);
square('3');
// @flow
function square(a) {
	return a * a;
}

square(3);
square('3');

function square(a: any): number {
	return a * a;
}

square(3);
square('3');
// @flow
function square(a: number | string): number {
	return a * a; // Error
}

square(3);
square('3');

function square(a: number): number {
	return a * a;
}

square(3);
square('3'); // Error
// @flow
function square(a: number): number { // Error
	return a * a;
}

square(3);
square('3'); // Error

function square(a) {
	if (Array.isArray(a)) {
		return a.map(square);
	}
	return a * a;
}

square(3);
square([3, 4, 5]);
square([3, '4', 5]);
square('3');
square(square(3));
// @flow
function square(a) {
	if (Array.isArray(a)) {
		return a.map(square);
	}
	return a * a;
}

square(3);
square([3, 4, 5]);
square([3, '4', 5]);
square('3');
square(square(3));

function square(a: any): any {
	if (Array.isArray(a)) {
		return a.map(square);
	}
	return a * a;
}

square(3);
square([3, 4, 5]);
square([3, '4', 5]);
square('3');
square(square(3));
// @flow
function square(a: number | string | Array<number|string>): number | number[] {
	if (Array.isArray(a)) {
		return a.map(square);
	}
	return a * a; // Error
}

square(3);
square([3, 4, 5]);
square([3, '4', 5]);
square('3');
square(square(3));

function square(a: number | number[]): number | number[] {
	if (Array.isArray(a)) {
		return a.map(square); // Err
	}
	return a * a;                 // Err
}

square(3);
square([3, 4, 5]);
square([3, '4', 5]);        // Error
square('3');                // Error
square(square(3));
// @flow
function square(a: number | number[]): number | number[] {
	if (Array.isArray(a)) {
		return a.map(square);    // Err
	}
	return a * a;
}

square(3);
square([3, 4, 5]);
square([3, '4', 5]);        // Error
square('3');                // Error
square(square(3));
// @flow
function square(a: number | string | [number, number | string, number]): number | number[] {
	if (Array.isArray(a)) {
		return a.map(square);
	}
	return a * a; // Error
}

square(3);
square([3, 4, 5]);
square([3, '4', 5]);
square('3');
square(square(3));

function square(a: number): number {
	return a * a;
}

function arrSquare(a: number | number[]): number | number[] {
	if (Array.isArray(a)) {
		return a.map(square);
	}
	return square(a);
}

arrSquare(3);
arrSquare([3, 4, 5]);
arrSquare([3, '4', 5]);    // Error
arrSquare('3');            // Error
arrSquare(arrSquare(3));
// @flow
function square(a: number): number {
	return a * a;
}

function arrSquare(a: number | number[]): number | number[] {
	if (Array.isArray(a)) {
		return a.map(square);
	}
	return square(a);
}

arrSquare(3);
arrSquare([3, 4, 5]);
arrSquare([3, '4', 5]); // Error
arrSquare('3');         // Error
arrSquare(arrSquare(3));
interface Foo {
    a: number,
    b: number,
    c?: number
}
type Foo = {
    a: number,
    b: ?number,
    c?: number,
}
const a: Foo = {
    a: 1,
    b: undefined,
    c: null
};
const a: Foo = {
    a: 1,
    b: undefined,
    c: null        // Error
};
const a: Foo = {
    a: 1,
    b: undefined,
};
const a: Foo = {
    a: 1,
    b: undefined,
};
const a: Foo = {
    a: 1,
    b: '2',    // Error
    c: 42
};
const a: Foo = {
    a: 1,
    b: '2',   // Error
    c: 42
};

Third Party Definitions

  • Kleine Collection
  • Sehr gute Qualität
  • Community basiert
  • Huge Collection
  • Durchwachsene Qualität
  • Microsoft pushed hier viel

http://definitelytyped.org/

https://github.com/flowtype/flow-typed

https://github.com/marudor/flowInterfaces

Config - Flow

[ignore]

[include]
src/

[libs]

[options]
esproposal.decorators=ignore
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
unsafe.enable_getters_and_setters=true
experimental.const_params=true
munge_underscores=true

module.system=haste
module.ignore_non_literal_requires=true

Resources

  • https://www.typescriptlang.org/
  • http://definitelytyped.org/
  • https://flowtype.org/
  • https://github.com/marudor/flowInterfaces
  • https://github.com/flowtype/flow-typed

JavaScriptChaos

By marudor

JavaScriptChaos

  • 618