ES6 v io.js

io.js – slavný fork Node.js nedávno vydal svou první verzi se sloganem „Přinášíme ES6 do komunity uzlů!“. io.js dostal tyto funkce před Node.js tím, že agresivně sledoval nejnovější verze V8 JavaScript motoru. Jako cizinec, který se dívá dovnitř, jsem si vzal pár hodin na to, abych se v tom pohrabal, a zde oznámím svá zjištění.

Instalace

Binární distribuce io.js jsou k dispozici na jejich titulní stránce a můžete si stáhnout binární soubor pro Linux, Windows, Mac nebo jej sestavit ze zdroje. Binární instalační programy však přepíší node a npm spustitelné soubory ve vašem systému, pokud máte nainstalovaný Node.js. Proto doporučuji použít nvm k instalaci io.js bezkonfliktně. Instalace nvm je docela snadná, pokud jste to ještě neudělali. Pokud a kdy máte nvm, jednoduše to udělejte

$ nvm install io.js
######################################################################## 100.0%
WARNING: checksums are currently disabled for io.js
Now using io.js v1.0.3

Zkontrolujte, zda to fungovalo:

$ iojs
>

Voilà! Všimněte si, že node má alias iojs a npm se stále nazývá npm .

Přehled funkcí ES6

Ačkoli někteří lidé již ES6 nějakou dobu používají prostřednictvím transpilerů, když pracuji s transpilovaným kódem, mám pocit, jako bych musel současně ladit dvě verze kódu – ladění je dost obtížné s jedinou verzí . Z tohoto důvodu je pro mě nativní podpora mnohem přitažlivější.

Stránka io.js ES6 poskytuje informace o změnách, které provedli v podpoře ES6 v enginu. Zrušili --harmony příznak - který jste v Node 0.11+ museli zahrnout, pokud jste vůbec chtěli používat nějaké funkce ES6. V io.js je získáte hned po vybalení! Aktuální seznam funkcí ES6 povolených ve výchozím nastavení je:

  • let prohlášení
  • const prohlášení
  • Map a Set
  • WeakMap a WeakSet
  • Generátory
  • Binární a osmičkové literály
  • Sliby
  • Některé další metody řetězců
  • Symboly
  • Řetězce šablon

Přidali také --es_staging příznak, který vám umožní získat přístup k funkcím, které jsou hotové, ale ještě nebyly dobře otestovány. U funkcí, které se právě implementují, byste museli získat přístup ke každé funkci jednotlivě pomocí příznaku harmonie, který jí odpovídá. Seznam příznaků funkcí harmonie můžete získat prostřednictvím:

$ iojs --v8-options|grep "harmony"
  --es_staging (enable all completed harmony features)
  --harmony (enable all completed harmony features)
  --harmony_shipping (enable all shipped harmony fetaures)
  --harmony_modules (enable "harmony modules (implies block scoping)" (in progress))
  --harmony_arrays (enable "harmony array methods" (in progress))
  --harmony_array_includes (enable "harmony Array.prototype.includes" (in progress))
  --harmony_regexps (enable "harmony regular expression extensions" (in progress))
  --harmony_arrow_functions (enable "harmony arrow functions" (in progress))
  --harmony_proxies (enable "harmony proxies" (in progress))
  --harmony_sloppy (enable "harmony features in sloppy mode" (in progress))
  --harmony_unicode (enable "harmony unicode escapes" (in progress))
  --harmony_tostring (enable "harmony toString")
  --harmony_numeric_literals (enable "harmony numeric literals")
  --harmony_strings (enable "harmony string methods")
  --harmony_scoping (enable "harmony block scoping")
  --harmony_classes (enable "harmony classes (implies block scoping & object literal extension)")
  --harmony_object_literals (enable "harmony object literal extensions")
  --harmony_templates (enable "harmony template literals")

Nyní přejdeme k jednotlivým funkcím.

let a const

let a const příkazy jsou dostupné pouze v přísném režimu. Zadejte tedy "use strict" v horní části každého souboru JS, kde je chcete použít.

let příkaz je náhradou za var výraz, který má lexikální rozsah. To znamená, že zatímco proměnná je definována pomocí var je viditelný pro funkci, ve které je deklarován, let je viditelný pouze pro blok kódu, ve kterém je deklarován. V JavaScriptu je blok kódu složený příkaz uzavřený v { a } který obsahuje nula nebo více příkazů. Bloky kódu běžně používáte v příkazech if, cyklech for, cyklech while a jako tělo definice funkce. Je však také možné napsat samostatný blok kódu.

Zde je příklad let :

"use strict"
if (player.partner){
  let partner = player.partner
  // do stuff with partner here
}
console.log(parter) // this throws partner is not defined

Zde je let ve smyčce for:

"use strict"
for (let i = 0; i < 10; i++){
  console.log(i)
}
console.log(i) // this throws i is not defined

const je jako let kromě toho, že jakmile je proměnná deklarována, nelze ji znovu přiřadit jiné hodnotě.

"use strict"
const ITERATIONS_TO_RUN = 10
ITERATIONS_TO_RUN = 12 // throws TypeError: Assignment to constant variable.

Mapa a sada

ES6 představil Map a Set datové struktury pro vaše pohodlí. Možná se teď ptáte, proč vůbec potřebujeme mapu? Co je špatného na používání objektových literálů jako map? No, argumentovalo se, že objekt není hash (nebo er mapa). Krátká verze je taková, že objekt zdědí všechny Object.prototype vlastnosti, což je ve většině případů nežádoucí, pokud jej chcete použít jako mapu.

Nyní je zde příklad použití Map :

> var m = new Map
undefined
> m.set('name', 'Bobby')
{}
> m.get('name')
Bobby
> m.size
1
> m.set('age', 5)
{}
> m.has('age')
true
> m.has('foobar')
false
> m.forEach(function(value, key){ console.log(key + ' maps to ' + value) })
name maps to Bobby
age maps to 5
> m.get('hasOwnProperty') // avoids the `hasOwnProperty` trap
undefined
> m.clear()
undefined
> m.size
0

A tady je Set in action:

> var s = new Set
undefined
> s.add(1)
{}
> s.size
1
> s.add(2)
{}
> s.size
2
> s.add(1) // adding a duplicate here
{}
> s.size   // no change in size
2
> s.has(1)
true
> s.has(2)
true
> s.has(3)
false
> s.forEach(function(n){ console.log('Set has ' + n) })
Set has 1
Set has 2

WeakMap a WeakSet

WeakMap a WeakSet jsou nové datové typy, které zrcadlí Map a Set , ale na rozdíl od Map a Set - které lze implementovat jako polyfilly - ty lze implementovat pouze nativně. Slovo „slabé“ odkazuje na slabé reference . Slabá reference je odkaz na objekt, který garbage collector ignoruje. Pokud existují pouze slabé reference – žádné další silné reference – ukazující na předmětný objekt, pak může být tento objekt zničen a jeho paměť opuštěna.

Pojďme se bavit o WeakSet za prvé - protože je to jednodušší vysvětlit. A WeakSet 's API je podmnožinou Set 's. Nemůžete do něj však uložit primitivní hodnoty:

> var ws = new WeakSet
undefined
> ws.add(1)
TypeError: Invalid value used in weak set

To dává smysl, protože primitivní hodnoty jsou uloženy hodnotou, nikoli odkazem, a nemělo by smysl ani mluvit o slabých odkazech. Místo toho do něj budete muset vložit objekty:

> var bob = {name: 'Bob'}
undefined
> var jen = {name: 'Jen'}
undefined
> ws.add(bob)
{}
> ws.add(jen)
{}
> ws.has(bob)
true
> ws.has(jen)
true
> var jim = {name: 'Jim'}
undefined
> ws.has(jim)
false
> ws.delete(jen)
true
> ws.has(jen)
false

WeakSet nemá size vlastnost nebo způsob iterace jejích členů

> ws.size
undefined
> ws.forEach(function(item){ console.log('WeakSet has ' + item)})
TypeError: undefined is not a function
> ws.forEach
undefined

Je to právě proto, že reference jsou slabé, a jako takové by mohly být objekty zničeny bez předchozího upozornění, v tomto okamžiku by k nim již nebylo možné získat přístup. Jedno z možných použití WeakSet je uložit sadu souvisejících prvků DOM bez obav z úniku paměti, když jsou prvky z dokumentu odstraněny.

A WeakMap je jako Map kromě toho, že všechny jeho klíče jsou slabé reference. Nesmí to být ani primitivní hodnoty.

var wm = new WeakMap
> var person = {name: 'Bob'}
undefined
> var creditCard = {type: 'AMEX', number: 123456789}
undefined
> wm.set(person, creditCard)
{}
> wm.get(person)
{ type: 'AMEX', number: 123456789 }

Stejně jako u Set neexistuje žádný způsob, jak získat size WeakMap nebo iterujte přes její klíče nebo hodnoty:

> wm.size
undefined
> wm.forEach
undefined

Když aplikace přestane obsahovat silný odkaz na person , jeho záznam v wm mohl být zničen a creditCard může být také zničen. Přečtěte si více o WeakMap a WeakSet.

for-of

Kromě klasického for-in ES6 přidal for-of příkaz, který vám umožní stručně iterovat hodnoty polí, iterovatelných položek a generátorů. Poslední dva budou diskutovány níže.

Zde je for-of iterace přes pole:

> var arr = [1, 2, 3]
undefined
> for (var n of arr) console.log(n)
1
2
3

Iterátory a iterátory

Můžete tedy také použít for-of příkaz iterovat přes iterovatelné.

Iterovatelný je objekt, který má přidruženou metodu, která inicializuje a vrací iterátor. Způsob přiřazení této metody k objektu je:

var myObj = {}
myObj[Symbol.iterator] = function(){  // I'll cover symbols later
  return new MyIterator
} 

Iterátor je objekt, který dodržuje protokol iterátoru – což vyžaduje pouze jednu metodu:

  • next() - která při každém zavolání postoupí na další položku v pořadí a vrátí objekt, který obsahuje dvě vlastnosti
  • done - boolean, který je pravdivý tehdy a jen tehdy, když sekvence již skončila
  • value - aktuální hodnota v sekvenci

Níže je uveden příklad, jak se mi podařilo vytvořit iterovatelnou jednoduchou vlastní implementaci seznamu odkazů:

function LLNode(value){
  this.value = value
  this.next = null
}
LLNode.prototype[Symbol.iterator] = function(){
  var iterator = {
    next: next
  }
  var current = this
  function next(){
    if (current){
      var value = current.value
      var done = current == null
      current = current.next
      return {
        done: done,
        value: value
      }
    }else{
      return {
        done: true
      }
    }
  }
  return iterator
}

var one = new LLNode(1)
var two = new LLNode(2)
var three = new LLNode(3)
one.next = two
two.next = three

for (var i of one){
  console.log(i)
}

Výstupem tohoto programu je

1
2
3

Generátory

Generátory vám umožňují psát iterovatelnou výstižným a snadno srozumitelným způsobem. Umožňuje také reprezentovat nekonečné sekvence.

Zde je návod, jak bych mohl napsat generátor, který iteruje všechna celá čísla počínaje 0:

function *naturalNumbers(){
  var n = 0
  while (true){
    yield n++
  }
}

Všimněte si function * syntaxe a yield příkaz – tyto označují, že se jedná o funkci generátoru spíše než normální funkce. Když zavoláte funkci generátoru, získáte zpět generátor, který implementuje protokol iterátoru:

> var gen = naturalNumbers()
{}
> gen.next()
{ value: 0, done: false }
> gen.next()
{ value: 1, done: false }

Je to také opakovatelné! Můžete si to ověřit:pokud zavoláte jeho metodu iterátoru, dostanete zpět samotný generátor:

> gen[Symbol.iterator]() === gen
true

Ale přesnější způsob, jak iterovat přes iterovatelný, je samozřejmě přes for-of prohlášení:

for (var n of naturalNumbers()){
  console.log(n)
}

Jejda! Nekonečná smyčka (facepalm).

Generátory jsou také skvělé, protože je to jedno řešení (mezi několika) problému zpětného volání. Co a koa jsou rámce, které intenzivně využívají generátory a oba fungují v io.js ihned po vybalení. Podrobnější zpracování generátorů naleznete v dalším textu.

Binární a osmičková čísla

Binární čísla mají předponu 0b a osmičkové číslice mají předponu 0O - tedy "nula" "O".

console.log(0b100)
console.log(0O100)

Výše uvedené výstupy programu:

4
64

Sliby

Vývoj příslibů byl do značné míry snahou zdola, začínal jako knihovny nebo komponenty v různých rámcích. Dnes existují zavedené knihovny jako RSVP, Q a Bluebird. Většina hlavních rámců má vestavěné sliby. Existuje standard pro sliby nazvaný Promises A+, který většina hlavních implementací dodržuje. Aby toho nebylo málo, sliby byly přeneseny do samotného běhového prostředí! Příběh za sliby je docela inspirativní.

Níže je uveden příklad, jak přeměnit knihovnu klienta http založenou na zpětném volání na funkci, která vrací příslib:

var request = require('superagent')

fetch('http://iojs.org')
  .then(function(reply){
    console.log('Returned ' + reply.text.length + ' bytes.')
  })

function fetch(url){
  return new Promise(function(resolve, reject){
    request(url).end(function(err, reply){
      if (err){
        reject(err)
      }else{
        resolve(reply)
      }
    })
  })
}

Sliby lze také efektivně využít s generátory – což je strategie, kterou spol. Přečtěte si tento návod, kde najdete podrobnější vysvětlení slibů.

Nové metody řetězců

Do nativního String byly přidány některé nové metody objekt.

  • String.fromCodePoint(number) a .codePointAt(idx) jsou jako String.fromCharCode a .charCodeAt(idx) kromě toho, že podporují unicode, a proto se vysoké kódové body převádějí do vícebajtových znaků

    > s = String.fromCodePoint(194564)
    '你'
    > s.codePointAt(0)
    194564
    
  • startsWith(s) a endsWith(s)

    > 'Hello, world!'.startsWith('Hello')
    true
    > 'Hello, world!'.endsWith('!')
    true
    
  • repeat(n)

    > 'foobar'.repeat(5)
    'foobarfoobarfoobarfoobarfoobar'
    
  • normalize() - vrátí formulář normalizace unicode struny. Abyste skutečně pochopili, co to znamená, přečtěte si o ekvivalenci unicode.

Symboly

Název symbol může být matoucí, protože tyto symboly nejsou jako ty v Ruby nebo Smalltalku. Symboly v ES6 se používají jako skryté vlastnosti objektů. Pokud jste Pythonista:přemýšlejte o magických metodách dvojitého podtržení.

var secretMethod = Symbol('secret')
var obj = {}
obj[secretMethod] = function(){
  return 'foobar'
}
obj[secretMethod]() // returns `foobar`

Nyní secretMethod nezobrazí se v rámci for-in procházet vlastnostmi objektu. Ve skutečnosti žádná vlastnost řetězce neodpovídá symbolu, na který odkazuje secretMethod a neexistuje způsob, jak přistupovat k metodě bez odkazu na symbol. V systému jsou globální "dobře známé" symboly jako Symbol.iterator - který jsme viděli použít k přidružení objektu k jeho iterátoru. V každém případě si přečtěte více o symbolech.

Řetězce šablon a víceřádkové řetězce

Řetězce šablony jsou vypůjčeny z interpolace řetězců Ruby a Perlu. Ušetří to vývojářům, aby museli nešikovně sčítat kousky řetězců – což často vede ke spoustě uvozovek.

> var name = 'Bobby'
undefined
> `Hello, ${name}!`
'Hello, Bobby!'

Všimněte si, že řetězce šablony jsou uzavřeny znaménky "`" spíše než jednoduchými nebo dvojitými uvozovkami - budete muset sáhnout nahoru levým malíčkem. Co je pro mě vzrušující je, že nyní můžete psát víceřádkové řetězce:

var age = 5
var sql = `
select
  name
from
  people
where
  age > ${age};
`

Řetězce šablon mají ještě jednu vlastnost – umožnit vlastní funkci vyhodnotit danou šablonu. To je užitečné v situacích, které vyžadují specifické escapování parametrů – například při dezinfekci parametrů SQL, aby se zabránilo útokům SQL injection.

var age = 5
var sql = sqlSanitize`
select
  name
from
  people
where
  age > ${age};
`

Můžete si přečíst více o hloubkovém zpracování řetězců šablony.

Významné funkce za vlajkami

Některé z významných funkcí jsou stále označeny jako probíhající v io.js – verze 1.0.3 v době psaní tohoto článku – jsou:

  • moduly – --harmony_modules
  • funkce šipek – --harmony_arrow_functions
  • proxy – --harmony_proxies
  • třídy – --harmony_classes

Celkový dojem

Cítím se optimisticky ohledně stavu funkcí ES6 na io.js. Líbí se mi, že všechny tyto funkce fungují hned po vybalení bez zvláštních příznaků. Mentálně toto označení činí tyto vlastnosti legitimními. Většinou, když jsou tyto funkce používány nesprávným způsobem, jsou vržené chybové zprávy užitečné při vedení uživatelů. Funkce, ze kterých jsem nejvíce nadšený, jsou generátory a řetězce šablon. Kdybych dnes začínal s novým hobby projektem, určitě bych zkusil io.js, zahrál si, šel do divočiny a vyzkoušel tyto funkce v divočině.