by lkokoszka@future-processing.com aka "Max"
var text = "𠮷"; // CJK UNIFIED IDEOGRAPH console.log(text.length); // 2 (surrogate pair) console.log(/^.$/.test(text)); // singe character test; false console.log(text.charAt(0)); // "" console.log(text.charAt(1)); // "" console.log(text.charCodeAt(0)); // 55362 console.log(text.charCodeAt(1)); // 57271
var text = "𠮷a"; console.log(text.charCodeAt(0)); // 55362 console.log(text.charCodeAt(1)); // 57271 console.log(text.charCodeAt(2)); // 97 console.log(text.codePointAt(0)); // 134071 console.log(text.codePointAt(1)); // 57271 console.log(text.codePointAt(2)); // 97
console.log("\u0061"); // "a" - aktualny standard
console.log("\u20BB7"); // "₻7" - próba użycia znaku Non-BMP
console.log("\u{20BB7}"); // " " - ECMAScript 6
/u - wymusza prace silnika wyrażeń regularnych w oparciu o znaki, nie "code units"
* wszystkie przyjmują parametr location, czyli punkt początkowy i zwracają wartość bool'owską
let oneLiner = `it is a one line`; // znak delimitacji ` let multiLiner_maybe_bad = `Multi // zachowuje się jak <pre> line text`; let multiLiner_maybe_good = ` // prosty trick Multi line text `.trim();
let what = 'dog',
with = 'a long tail',
// używamy zmiennych "osiągalnych" w scope'ie
msg1 = `it is a ${what}`,
msg2 = `it is a ${what} with ${with}`;
console.log(msg1); // "it is a dog"
console.log(msg2); // "it is a dog with a long tail"
let howMuch = 20,
exchangeRate = 4,
convert = function (eur, exRate) {
return (eur * exRate).toFixed(2);
},
euro2pln = `it is ${(howMuch * exchangeRate).toFixed(2)} PLN`;
euro2plnFn = `it is ${convert(howMuch,exchangeRate)} PLN`;
console.log(euro2pln); // "it is 80.00 PLN"
console.log(euro2plnFn); // "it is 80.00 PLN"
tag`Hello ${name}`
function extRangeTrans(literals, ...substitutions) {
let result = "", i;
for (i = 0; i < substitutions.length; i++) {
result += literals[i];
result += substitutions[i];
}
result += literals[literals.length - 1];
return result;
}
let pln = 20,
exRate = 4.2,
euro2pln = extRangeTrans`
${eur} EUR (@ ${exRate}) is
${(eur * exRate).toFixed(2)} PLN
`.trim();
console.log(euro2pln); //20 EUR (@ ${exRate}) is 84.00 PLN
`a\nb` // a // b String.raw`a\nb` //a\nb
var text = 'First line\nsecond line'; var regex = /(\S+) line\n?/y; var match = regex.exec(text); match[1] // 'First' regex.lastIndex // '11' var match2 = regex.exec(text); match2[1] // 'Second' regex.lastIndex // '22' var match3 = regex.exec(text); match3 === null // 'true'
var re1 = /ab/i,
re2 = new RegExp(re1, "g");
var value1 = 0o71; // 57
var value2 = 0b101; // 5
parseInt("0o71"); // 0
parseInt("0b101"); // 0
Number("0o71"); // 57
Number("0b101"); // 5
isFinite(25) // true
isFinite("25") // true
Number.isFinite(25) // true
Number.isFinite("25") // false
isNaN(NaN) // true
isNaN("NaN") // true
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isInteger(25) // true Number.isInteger(25.0) // true Number.isInteger(25.1) // false Number.isSafeInteger(9007199254740991) // true Number.isSafeInteger(9007199254740992) // false
let color = 'red';
if (true) {
console.log(color) // ReferenceError
let color = 'white';
console.log(color) // "white"
}
console.log(color) // "red"
użycie var
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push( function () { console.log(i); });
}
funcs.forEach(function (func) {
func(); // wypluje liczbę "10" 10x
});
verte ↓
użycie let
var funcs = [];
for (let i = 0; i < 10; i++) {
funcs.push( function () { console.log(i); });
}
funcs.forEach(function (func) {
func(); // wypluje liczbę "0", potem "1" itd.
});
const MAX_SIZE = 'red';
var sourceObject = {from: 1, to: 2}
function ECMAScript5 (sourceObject) {
var localFrom = sourceObject.from,
localTo = sourceObject.to;
}
function ECMAScript6 (sourceObject) {
let { from: localFrom, to: localTo } = sourceObject;
console.log(localFrom, localTo); // :)
// lub w przypadku mapowania nazwenictwa 1:1
let { from, to } = sourceObject;
console.log(from, to); // :D
}
{ wlasciwoscObiektuZrodlowego: zmienneLokalna} = obiektZrodlowy
var sourceObject = { from: 1, to: 2, more: { max: 3 } };
// później gdzieś w kodziwie...
let { from, to, more: { max }} = sourceObject;
console.log(from, to, max);
// :D - ! `more` nie zostanie zadeklarowane
var kolory = ['red', 'green', 'blue']; // później gdzieś w kodziwie... let [firstColor, secondColor] = kolory; console.log(firstColor, secondColor) // :D[ pierwszyElement, drugiElement...] = tablicaZrodlowa
var sourceObject = { from: 1, to: 2, colors: [
'red', 'green', 'blue'
]};
// później gdzieś w kodziwie...
let {from, to, colors: [firstColor, secondColor]} = kolory;
console.log(from, to, firstColor, secondColor) // :D
let {from, to, colors} = kolory;
console.log(from, to, colors)
// :D - colors będzie referencją do sourceObject.colors
function reload (what, in = 2000, then = function () {} ) { }
function defaultAddWhatValue () { return 0; }
function add (addTo, addWhat = defaultAddWhatValue() + 0) { }
function sampleRest (param1, param2, ...options) {
...
console.log(options.length);
// ^ == przekazane - zadeklarowane nazwane
}
function destParams (param1, {setting1, setting2}, {setting3} = {}) {
console.log(param1, setting1, setting2, setting3);
}
destParams(1); // error!
destParams(1, {setting1: 'a', setting2: 'b'});
var params = ['a', 'b'];
function yYoOeE (param1, param2) { }
// zamiast
yYoOeE.apply(yYoOeE, params);
// szkrótem!
yYoOeE(...params);
var doSomething = function doSomethingElse() { };
// doSomething.name == "doSomethingElse"
function doSomething () { }
// doSomething.name == "doSomething"
var doAnotherThing = function () { };
// doAnotherThing.name == "doAnotherThing"
var person = { get firstName () { }, sayName: function () { }};
// person.firstName.name == "get firstName"
// person.sayName.name == "sayName"
var test = doSomething.bind(null);
// test.name == "bound doSomething"
// (function () {}).name == "anonymous"
"use strict";
if ( true ) {
console.log( typeof doSomething); // "function"
function doSomething() { }
}
console.log( typeof doSomething);
// "undefined"
var justReturn = value => value;
// jest równoznaczne z:
var justReturn = function (value) { return value; };
[0,1,2].forEach(v => {
results.push(v * 2);
});
var test = () => 'test';
var add = (param1, param2) => { return param1 + param2 };
var addFn = add() => { return add() };
* wciąż dyskutowane
let local = 1,
example = { local };
// example == { local: 1 };
let example = {
do() {
//...
}
}
// typeof example.local == 'function'
let errorSuffix = 'Error',
errorSuffixProvider = () => 'Error',
example = {
['profile' + 'Error']: false,
['user' + errorSuffix]: false,
['network' + errorSuffixProvider()]: false
};
// example = {
// profileError: false, userError: false, networkError: false
// }
let person = {
getGreeting() { return "Hello"; }
};
let dog = {
getGreeting() { return "Woof"; }
};
let friend = Object.create(person);
friend.getGreeting() // "Hello"
Object.getPrototypeOf(friend) === person // true
Object.setPrototypeOf(friend, dog);
friend.getGreeting() // "Woof"
Object.getPrototypeOf(friend) === dog // true
// metoda getEmployer na instancji Developer
// którego prototypem jest Employee
getEmployer() {
Developer.prototype.getEmployer.call(this);
// lub
this.__proto__.getEmployer.call(this)
// lub
Object.getPrototypeOf( this ).getEmployer.call( this );
// lub w ECMAScript 6
super.getEmployer(); // === super.getEmployer.call(this)
// lub
super()
}
super referencja wprowadza konieczność zdefiniowana nowej ukrytej/wew. właściwiość funkcji/metody oznaczonej w dokumentacji jako [[HomeObject]]. Ta wskazuje na obiekt do którego dana funkcja/metoda należy i jest ustawiana w chwili definiowania funkcji.
To zmienia definicję metody w JS.
"ECMAScript 6 formally defines a method as a function that has an internal [[HomeObject]] property containing the object to which the method belongs."" zatem w każdym inny przypadku mamy doczynienia z funkcją!ponieważ [[HomeObject]] jest stricte związany z procesem definiowana funkcji, aby móc korzystać z dobordziejstw super referencji w przypadku nadpisywania metod obiektu w czasie jego życia, należy użyć nowej metody dostępnej dla każdej funkcji .toMethod(), która skonwertuje funkcję na metodę.
let friend = {
sayHi() {
return "hi!";
}
};
function sayHiAndYo () {
return super.sayHi() + ", yo!";
}
friend.sayHi = sayHiAndYo;
friend.sayHi(); // error!
friend.sayHi = sayHiAndYo.toMethod(friend); // :)
var sym1 = Symbol(),
sym2 = Symbol("foo"),
sym3 = Symbol("foo"),
sym4 = new Symbol(), // TypeError
sym5 = Symbol.for('meta_data_storage');
// pierwszy lookup utworzy nowy symbol o danym opisie/kluczu
// typeof sym1 == "symbol"
// typeof sym3 == "symbol"
// typeof sym5 == "symbol"
- symbol może zawierać opis/klucz (przechowywany wew.), który:
w przypadku symboli globalnych może posłużyć do jego odszukania w rejestrze,
w przypadku lokalnych może przydać się w procesie debuggowania
- ponieważ Symbol nie jest konstruktorem nie można używać go z operatorem `new`
var sym = Symbol.for('meta_data_storage');
// pierwszy lookup utworzy nowy symbol o danym opisie/kluczu
console.log(Symbol.keyFor(sym)); // "meta_data_storage"
w przypadku użycia keyFor z symbolem lokalnym metoda zwróci undefined
to lista symboli wykorzystywanych w silniku JS i opisanych w specyfikacji jako dostępnych dla programisty w celu umożliwenia zmian w część wewnętrznych zachowań języka. Spec. posługuje się notacją "@@nazwaSymbolu" dla odróżnienia symboli od innych danych
function Person(name) {
this.name = name;
}
Person.prototype[Symbol.toStringTag] = "Person";
Person.prototype.toString = function () {
return this .name;
};
var me = new Person("Nicholas");
console.log(me.toString()); // "Nicholas"
console.log(Object.prototype.toString.call(me)); // "[object Person]
ponieważ Object.getOwnPropertyNames nie zwróci właściwości zdefniowanych za pomocą symboli, aby uzyskać do nich dostęp należy użyć Object.getOwnPropertySymbols, który zwróci array symboli
function createIterator(items) {
var i = 0;
return {
next: function () {
var done = (i >= items.length),
value = !done ? items[i++] : undefined;
return { done: done, value: value };
}
};
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }
definicja takiego iteratora jest możliwa już w ECMAScript 5, jedynie
ostatni przypadek jest szczególny i związany z oczekiwaniami ECMAScript 6
// generator
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
var obj = {
es5: function * () {},
*es6() {}
};
class A {
*giveMe () {}
}
// generator
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
let iterator = createIterator();
for ( let i of iterator ) {
console.log(i);
}
// generator
function *createIterator() {
yield 1;
return 100;
yield 2;
yield 3;
}
to jednak zadziała tylko przy manualnej iteracji - for..of ignoruje zwróconą wartość
function *createNumberIterator() {
yield 1;
yield 2;
}
function *createCombinedIterator() {
yield *createNumberIterator();
yield true ;
}
var iterator = createCombinedIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: true, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
function *createIterator() {
yield * "test";
}
class Example extends AnotherExample {
constructor () {
super();
}
methodA () {}
static methodB () {}
*[Symbol.iterator]() {}
}
var c = class C {};
class Example {
constructor () {
this.property = true;
}
get property () {}
set property () {}
}
ES6 definuje domyślny konstruktor
class Example {
static test () {
return 'test';
}
}
Example.test();
class Example extends AnotherExample {}
class Example extends combine(AnotherExample, BaseExample) {}
można dziedziczyć po null jak i dowolnym obiekcie (nie koniecznie zdefiniowany z użyciem class; np. Array)
ES6 wraz z obsługą klas wprowadza bardzo ważną zmianę związaną z tworzeniem ich instancji:instancja jest tworzona w najgłębiej położonym konstruktorze całego łańcucha dziedziczenia podczas gdy ES5 tworzył instancje zaraz po wywołaniu operatora new
verte ↓
to wymóg w przypadku wykorzystywania dziedziczenia
class Example extends AnotherExample {
constructor () {
this.something = true; // ReferenceError
super()
// WYMAGANE WYWOŁANIE konstruktora klasy nadrzędnej
// za pomocą "super referencji"
this.something = true;
}
}
nowy "niejawny parametr" dostępna we wszystkich funkcjach. Dla konstruktorów klas jest ref. na funkcję wywołującą.`new.target` jest dla konstruktorów tym czym`this` jest dla funkcji
class Example extends AnotherExample {
constructor () {
super()
// `new.target` w AnotherExample wskazuje
// na konstruktor Example
}
}
- new.target.prototype staję prototype tworzonej instacji dzięki czemu wszystkie
egzotyczne "właściwości" mogą zostać prawidołow zainicjalizowane
- w przypadku normalnych funkcji new.target == undefined, dla "arrow functions" new.target == new.target otaczającego scope'a
[1]
z zachowaniem ich "magicznych" właściwości jak np. Array
class MyArray extends Array {
constructor () {
super()
}
}
(new MyArray()).length // będzie działać prawidłowo!
ten feature wymaga jednak zmian w silnikach JS!
tj. nadpisanie konstruktora używanego przez metody zwracające nowe instancje, jak np. Array.prototype.map
class MyArray1 extends Array { }
let result1 = new MyArray1().map(x => x);
result1 instanceof MyArray1 // true
class MyArray2 extends Array {
static get [Symbol.species]() {
return Array;
}
}
let result2 = new MyArray2().map(x => x);
result2 instanceof MyArray2 // false
var sample = new Set([1, "1", true, {}]); // iterable jako parametr
sample.size // 4
z użyciem for..of lub forEach
var obj = {},
sample = new WeakSet([obj]); // iterable jako parametr
var sample = new Map([["key1", "value1"], ["key2", "value2"]]);
sample.set("key3", "value3");
sample.get("key3");
z użyciem for..of lub forEach
var obj = {},
sample = new WeakMap([[obj, 'test']]);
// iterable jako parametr
let target = {},
handler = {
get (target, propKey, receiver) {
console.log('get ' + propKey);
return 123;
},
ownKeys (target) {
console.log('ownKeys');
return ['hello', 'world'];
}
},
proxy = new Proxy(target, handler);
proxy.foo // "get foo" -> 123
Object.keys(proxy) // "ownKeys" -> "['hello', 'world']"
pułapka get jest na swój sposób szczególna, przez nią bowiem przechodzi większość akcji wykonywanych na obiektach zatem można jej używać do całościowego przeciążania obiektów
let proxy = new Proxy({
a: true,
b: function () {}
}, {
get(target, propKey, receiver) {
console.log('get ' + propKey);
return target[propKey];
}
});
proxy.a; // "get a" + true
proxy.b(); // "get b" + 'call to b'
proxy może zostać użyte jako prototyp obiektu, dzięki czemu można wyłapywać operacje, które nie zostały zaspokojony przez obiekt lub wymusiły lookup po łańcuchu prototypów
let proto = new Proxy({}, {
get(target, propertyKey, receiver) {
console.log('GET ' + propertyKey);
return target[propertyKey];
}
});
let obj = Object.create(proto);
obj.bla; // GET bla
let handler = {
deleteProperty(target, propKey) {
console.log('DELETE ' + propKey);
return Reflect.deleteProperty(target, propKey);
},
has(target, propKey) {
console.log('HAS ' + propKey);
return Reflect.has(target, propKey);
}
};
let proxy = new Proxy({}, handler);
delete proxy.test; // DELETE test
var promise = new Promise(function (resolve, reject) {
// tzw. "executor"
resolve(); //jeśli poszło OK
reject(); //jeśli coś poszło nie tak
});
promise.then(r => 1).then(r => ({})).then(r => true);
promise.then(r => 1).catch(e => ({})).then(r => true);
Promise.all([promise1, promise2, promise3]); Promise.race([promiseDownload1, promiseDownload2, promiseDownload3]);
// modules/A.js
export default function () {};
// modules/B.js
export function print() {};
// modules/C.js
import A from 'modules/A';
import {print} from 'modules/B';
export default function () {};
// modules/D.js
import C from 'modules/C';
C();
// modules/A.js
export default function () {};
export function test() {};
export class testClass() {};
// modules/B.js
function doSomething() {}
export doSomething;
// modules/C.js
const MY_CONST = ...;
function myFunc() {}
export { MY_CONST, myFunc };
moduły mogą wykonywać wiele nazwanych exportów i nie więcej niż jeden domyślny (via default).
można łączyć eksport domyślny z nazwanymi w jednym module (świetnym przykładem wykorzystania mieszanego exportu są underscore.js i lodash.js)
// modules/A.js
export function doSomething() {}
export {doSomething as justDo};
const MY_CONST = ...;
export { MYCONST as THATCONST };
// lub
export { MYCONST as default };
import {default as theDefault} from 'modules/B';
// ==
import theDefault from 'modules/B';
// pozostałe możliwości
import theDefault, {named1, named2} from 'modules/B';
import { named1, named2 } from 'modules/B';
// aliasowanie / zmiana nazwy podczas importu
import { named1 as moduleBFn };
// importowanie modułu do obiektu/tworzenie "namespace"
import * as moduleB from 'modules/B';
// ładowanie bez importu
import 'modules/B';
function add5(a) {
return a + 5;
}
function add10 (a){
return add5(a); // tail call
return 5 * add5(a); // a tutaj nie
if (a > 5) {
return add(10); // recursive tail call
}
}
function computeMaxCallStackSize() {
try {
return 1 + computeMaxCallStackSize();
} catch (e) {
// Call stack overflow
return 1;
}
}
function computeMaxCallStackSize(size) {
size = size || 1;
return computeMaxCallStackSize(size + 1);
}