Aprender ES2015
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Este documento se basó originalmente en el excelente repositorio es6features de Luke Hoban. ¡Dale una estrella en GitHub!
Asegúrate de probar estas características en el REPL en línea.
Introducción
ECMAScript 2015 es un estándar ECMAScript ratificado en junio de 2015.
ES2015 es una actualización significativa del lenguaje, y la primera actualización importante desde que ES5 se estandarizó en 2009. La implementación de estas características en los principales motores JavaScript está en curso actualmente.
Consulta el estándar ES2015 para la especificación completa del lenguaje ECMAScript 2015.
Características de ECMAScript 2015
Funciones Flecha y this Léxico
Las funciones flecha son una sintaxis abreviada que usa =>. Son sintácticamente similares a características relacionadas en C#, Java 8 y CoffeeScript. Admiten tanto cuerpos de expresión como de declaración. A diferencia de las funciones normales, las flechas comparten el mismo this léxico que el código circundante. Si una flecha está dentro de otra función, comparte la variable "arguments" de su función padre.
// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
// Statement bodies
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// Lexical this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};
// Lexical arguments
function square() {
let example = () => {
let numbers = [];
for (let number of arguments) {
numbers.push(number * number);
}
return numbers;
};
return example();
}
square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]
Clases
Las clases de ES2015 son azúcar sintáctico sobre el patrón OO basado en prototipos. Tener una única forma declarativa conveniente facilita el uso de patrones de clases y fomenta la interoperabilidad. Las clases admiten herencia basada en prototipos, llamadas super, métodos estáticos y de instancia, y constructores.
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}
Literales de Objeto Mejorados
Los literales de objeto se extienden para permitir establecer el prototipo durante la construcción, abreviar asignaciones foo: foo, definir métodos y hacer llamadas super. En conjunto, estos cambios acercan los literales de objeto y las declaraciones de clase, permitiendo que el diseño basado en objetos se beneficie de conveniencias similares.
var obj = {
// Sets the prototype. "__proto__" or '__proto__' would also work.
__proto__: theProtoObj,
// Computed property name does not set prototype or trigger early error for
// duplicate __proto__ properties.
['__proto__']: somethingElse,
// Shorthand for ‘handler: handler’
handler,
// Methods
toString() {
// Super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
[ "prop_" + (() => 42)() ]: 42
};
La propiedad proto requiere soporte nativo y fue desaprobada en versiones anteriores de ECMAScript. La mayoría de motores actuales la admiten, pero some do not. Además, ten en cuenta que solo los web browsers están obligados a implementarla, ya que está en el Annex B. Está disponible en Node.
Plantillas de Cadena de Texto
Las plantillas de cadena proporcionan azúcar sintáctico para construir cadenas. Esto es similar a características de interpolación en Perl, Python y otros. Opcionalmente, se puede agregar una etiqueta para personalizar la construcción de cadenas, evitando ataques de inyección o construyendo estructuras de datos complejas a partir de contenido de cadenas.
// Basic literal string creation
`This is a pretty little template string.`
// Multiline strings
`In ES5 this is
not legal.`
// Interpolate variable bindings
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// Unescaped template strings
String.raw`In ES5 "\n" is a line-feed.`
// Construct an HTTP request prefix is used to interpret the replacements and construction
GET`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
Desestructuración
La desestructuración permite enlazar mediante coincidencia de patrones, con soporte para arrays y objetos. Es tolerante a fallos, similar a la búsqueda estándar de objetos foo["bar"], produciendo valores undefined cuando no se encuentra.
// list matching
var [a, ,b] = [1,2,3];
a === 1;
b === 3;
// object matching
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()
// object matching shorthand
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = getASTNode()
// Can be used in parameter position
function g({name: x}) {
console.log(x);
}
g({name: 5})
// Fail-soft destructuring
var [a] = [];
a === undefined;
// Fail-soft destructuring with defaults
var [a = 1] = [];
a === 1;
// Destructuring + defaults arguments
function r({x, y, w = 10, h = 10}) {
return x + y + w + h;
}
r({x:1, y:2}) === 23
Parámetros Predeterminados + Rest + Spread
Valores predeterminados evaluados en el contexto del llamante. Convierte un array en argumentos consecutivos en una llamada de función. Agrupa parámetros finales en un array. Rest reemplaza la necesidad de arguments y aborda casos comunes más directamente.
function f(x, y=12) {
// y is 12 if not passed (or passed as undefined)
return x + y;
}
f(3) == 15
function f(x, ...y) {
// y is an Array
return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6
Let + Const
Constructos de enlace con ámbito de bloque. let es el nuevo var. const es de asignación única. Restricciones estáticas evitan su uso antes de la asignación.
function f() {
{
let x;
{
// this is ok since it's a block scoped name
const x = "sneaky";
// error, was just defined with `const` above
x = "foo";
}
// this is ok since it was declared with `let`
x = "bar";
// error, already declared above in this block
let x = "inner";
}
}
Iteradores + For..Of
Los objetos iteradores permiten iteración personalizada como CLR IEnumerable o Java Iterable. Generalizan for..in para iteración personalizada basada en iteradores mediante for..of. No requieren materializar un array, permitiendo patrones de diseño perezosos como LINQ.
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
La iteración se basa en estas interfaces de tipo pato (usando sintaxis de tipos de TypeScript solo para exposición):
interface IteratorResult {
done: boolean;
value: any;
}
interface Iterator {
next(): IteratorResult;
}
interface Iterable {
[Symbol.iterator](): Iterator
}
Para usar Iteradores, debes incluir el polyfill de Babel.
Generadores
Los generadores simplifican la creación de iteradores usando function* y yield. Una función declarada como function* devuelve una instancia de Generator. Los generadores son subtipos de iteradores que incluyen next y throw adicionales. Permiten que valores fluyan de vuelta al generador, haciendo de yield una forma de expresión que devuelve un valor (o lanza excepción).
Nota: También pueden usarse para habilitar programación asíncrona similar a 'await', consulta también la propuesta ES7 await propuesta.
var fibonacci = {
[Symbol.iterator]: function*() {
var pre = 0, cur = 1;
for (;;) {
var temp = pre;
pre = cur;
cur += temp;
yield cur;
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
La interfaz del generador es (usando sintaxis de tipos de TypeScript para exposición):
interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}
Para usar Generadores, debes incluir el polyfill de Babel.
Comprensiones
Eliminado en Babel 6.0
Unicode
Adiciones no disruptivas para soporte completo de Unicode, incluyendo nueva forma literal unicode en cadenas y nuevo modo u en RegExp para manejar puntos de código, así como nuevas APIs para procesar cadenas a nivel de puntos de código de 21 bits. Estas adiciones soportan construcción de aplicaciones globales en JavaScript.
// same as ES5.1
"𠮷".length == 2
// new RegExp behaviour, opt-in ‘u’
"𠮷".match(/./u)[0].length == 2
// new form
"\u{20BB7}" == "𠮷"
"𠮷" == "\uD842\uDFB7"
// new String ops
"𠮷".codePointAt(0) == 0x20BB7
// for-of iterates code points
for(var c of "𠮷") {
console.log(c);
}
Módulos
Soporte a nivel de lenguaje para módulos para definición de componentes. Codifica patrones de cargadores de módulos JavaScript populares (AMD, CommonJS). Comportamiento en tiempo de ejecución definido por un cargador predeterminado definido por el host. Modelo implícitamente asíncrono: ningún código se ejecuta hasta que los módulos solicitados estén disponibles y procesados.
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
console.log("2π = " + sum(pi, pi));
Algunas características adicionales incluyen export default y export *:
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
// app.js
import exp, {pi, e} from "lib/mathplusplus";
console.log("e^π = " + exp(pi));
Babel puede transpilar Módulos ES2015 a varios formatos diferentes incluyendo Common.js, AMD, System y UMD. Incluso puedes crear el tuyo. Para más detalles consulta la modules docs.
Cargadores de Módulos
Esto queda definido por la implementación en la especificación ECMAScript 2015. El estándar definitivo estará en la Loader specification de WHATWG, pero actualmente es trabajo en progreso. Lo siguiente es de un borrador anterior de ES2015.
Los cargadores de módulos soportan:
-
Carga dinámica
-
Aislamiento de estado
-
Aislamiento del espacio de nombres global
-
Hooks de compilación
-
Virtualización anidada
El cargador de módulos predeterminado puede configurarse, y se pueden construir nuevos cargadores para evaluar y cargar código en contextos aislados o restringidos.
// Dynamic loading – ‘System’ is default loader
System.import("lib/math").then(function(m) {
alert("2π = " + m.sum(m.pi, m.pi));
});
// Create execution sandboxes – new Loaders
var loader = new Loader({
global: fixup(window) // replace ‘console.log’
});
loader.eval("console.log(\"hello world!\");");
// Directly manipulate module cache
System.get("jquery");
System.set("jquery", Module({$: $})); // WARNING: not yet finalized
Como Babel usa módulos common.js por defecto, no incluye el polyfill para la API del cargador de módulos. Consíguelo here.
Para usar esto, necesitarás indicar a Babel que use el formateador de módulos system. También asegúrate de revisar System.js.
Map + Set + WeakMap + WeakSet
Estructuras de datos eficientes para algoritmos comunes. Los WeakMaps proporcionan tablas laterales con claves de objetos sin fugas de memoria.
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in the set
Para dar soporte a Maps, Sets, WeakMaps y WeakSets en todos los entornos, debes incluir el polyfill de Babel.
Proxies
Los Proxies permiten crear objetos con todo el rango de comportamientos disponibles para objetos host. Se pueden usar para intercepción, virtualización de objetos, registro/profilado, etc.
// Proxying a normal object
var target = {};
var handler = {
get: function (receiver, name) {
return `Hello, ${name}!`;
}
};
var p = new Proxy(target, handler);
p.world === "Hello, world!";
// Proxying a function object
var target = function () { return "I am the target"; };
var handler = {
apply: function (receiver, ...args) {
return "I am the proxy";
}
};
var p = new Proxy(target, handler);
p() === "I am the proxy";
Existen trampas disponibles para todas las meta-operaciones a nivel de tiempo de ejecución:
var handler =
{
// target.prop
get: ...,
// target.prop = value
set: ...,
// 'prop' in target
has: ...,
// delete target.prop
deleteProperty: ...,
// target(...args)
apply: ...,
// new target(...args)
construct: ...,
// Object.getOwnPropertyDescriptor(target, 'prop')
getOwnPropertyDescriptor: ...,
// Object.defineProperty(target, 'prop', descriptor)
defineProperty: ...,
// Object.getPrototypeOf(target), Reflect.getPrototypeOf(target),
// target.__proto__, object.isPrototypeOf(target), object instanceof target
getPrototypeOf: ...,
// Object.setPrototypeOf(target), Reflect.setPrototypeOf(target)
setPrototypeOf: ...,
// Object.keys(target)
ownKeys: ...,
// Object.preventExtensions(target)
preventExtensions: ...,
// Object.isExtensible(target)
isExtensible :...
}
Debido a las limitaciones de ES5, los Proxies no se pueden transpilar ni aplicar mediante polyfill. Consulta el soporte en various JavaScript engines.
Symbols
Los Symbols permiten control de acceso al estado de objetos. Permiten que las propiedades se indexen mediante string (como en ES5) o symbol. Son un nuevo tipo primitivo. El parámetro opcional name se usa en depuración pero no forma parte de la identidad. Los Symbols son únicos (como gensym), pero no privados ya que se exponen mediante características de reflexión como Object.getOwnPropertySymbols.
(function() {
// module scoped symbol
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
... this[key] ...
}
};
// Limited support from Babel, full support requires native implementation.
typeof key === "symbol"
})();
var c = new MyClass("hello")
c["key"] === undefined
El soporte limitado requiere el polyfill de Babel. Debido a limitaciones del lenguaje, algunas características no se pueden transpilar ni aplicar mediante polyfill. Consulta la caveats section de core.js para más detalles.
Clases integradas que pueden extenderse
En ES2015, las clases integradas como Array, Date y los Element del DOM pueden extenderse mediante herencia.
// User code of Array subclass
class MyArray extends Array {
constructor(...args) { super(...args); }
}
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
La capacidad de extender clases integradas debe evaluarse caso por caso: clases como HTMLElement can extenderse, mientras que Date, Array y Error cannot debido a limitaciones de motores ES5.
APIs de Math, Number, String y Object
Numerosas adiciones a bibliotecas, incluyendo núcleos matemáticos, ayudantes para conversión de arrays, y Object.assign para copia.
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"
Array.from(document.querySelectorAll("*")) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1,2,3].findIndex(x => x == 2) // 1
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"
Object.assign(Point, { origin: new Point(0,0) })
Literales binarios y octales
Se añaden dos nuevas formas de literales numéricos para binario (b) y octal (o).
0b111110111 === 503 // true
0o767 === 503 // true
Babel solo puede transformar 0o767 y no Number("0o767").
Promesas
Las Promesas son una biblioteca para programación asíncrona. Representan valores que podrían estar disponibles en el futuro. Se usan en muchas bibliotecas JavaScript existentes.
function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
})
}
var p = timeout(1000).then(() => {
return timeout(2000);
}).then(() => {
throw new Error("hmm");
}).catch(err => {
return Promise.all([timeout(100), timeout(200)]);
})
Para dar soporte a Promesas, debes incluir el polyfill de Babel.
API Reflect
API de reflexión completa que expone las metaoperaciones a nivel de tiempo de ejecución sobre objetos. Esta funcionalidad es esencialmente la inversa de la API Proxy, y permite realizar llamadas correspondientes a las mismas metaoperaciones que las trampas de los proxies. Es especialmente útil para implementar proxies.
var O = {a: 1};
Object.defineProperty(O, 'b', {value: 2});
O[Symbol('c')] = 3;
Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]
function C(a, b){
this.c = a + b;
}
var instance = Reflect.construct(C, [20, 22]);
instance.c; // 42
Para usar la API Reflect debes incluir el polyfill de Babel.
Llamadas de Cola
Las llamadas en posición de cola garantizan que la pila no crezca indefinidamente. Hace que los algoritmos recursivos sean seguros frente a entradas de tamaño ilimitado.
function factorial(n, acc = 1) {
"use strict";
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
// Stack overflow in most implementations today,
// but safe on arbitrary inputs in ES2015
factorial(100000)
Solo se admitía recursión de cola explícita autorreferenciada debido a la complejidad y el impacto en el rendimiento de soportar llamadas de cola globalmente. Se eliminó por otros errores y será reimplementado.