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 | |
Closure | |
Traceur | |
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
GlobalsAMD (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
- 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