Skip to content

JavaScript, EcmaScript, novedades y recomendaciones del lenguaje

El lenguaje para scripting en adquio es EcmaScript (abreviado como ES y también conocido históricamente como JavaScript). En este documento no se explicarán la mayoría de construcciones del lenguaje.

Nota: Aunque el lenguaje es estándar, algunos objetos globales, como "console" no son accesibles desde los scripts.

Algunas nociones sobre las últimas versiones de ES

El motor de scripting de adquio permite funcionalidades modernas del lenguaje, con las que puede no estar habituado un desarrollador o estudiante de JavaScript tradicional. Aunque la sintaxis sin estos añadidos es perfectamente válida, documentamos a continuación algunas de las novedades que pueden resultar más útiles.

Funciones flecha o arrow functions

En adquio se prefiere el uso de la sintaxis "flecha" para definir funciones. En sus efectos las funciones con flecha son casi equivalentes a funciones tradicionales JS. Tienen, sin embargo, una ligera ventaja en rendimiento (al evitar un cambio de contexto) y son más legibles en ciertas circunstancias.

Podría a su vez clasificarse las arrow functions en dos clases, a continuación ejemplos de equivalencia en notación tradicional y con flecha.

  • De resultado inmediato, que solo ejecutan una sentencia y devuelven ese mismo valor. No usan llaves.
// Función en sintaxis tradicional
function suma(a, b) {
  return a + b;
}
// Sintaxis arrow function abreviada, asignando a variable
const suma = (a, b) => a + b;
// Sintaxis arrow function abreviada, usada como parámetro
funcionQueUsaSuma((a,b) => a + b);
  • En un sentido más tradicional, incluyendo varias líneas y sentencias. Se han de incluír llaves para delimitar su cuerpo.
// Función en sintaxis tradicional
function activarClima() {
  const tExterior = devices.read('SONDAS', 'EXTERIOR');
  if(tExterior > 19) {
    devices.write('CLIMA', 'ONOFF', true);
  } else {
    logger.debug('El clima no necesita encenderse');
  }
  return devices.read('CLIMA', 'ONOFF');
}
// Sintaxis arrow function
const activarClima = () => {
  const tExterior = devices.read('SONDAS', 'EXTERIOR');
  if(tExterior > 19) {
    devices.write('CLIMA', 'ONOFF', true);
  } else {
    logger.debug('El clima no necesita encenderse');
  }
  return devices.read('CLIMA', 'ONOFF');
}

Nota: Cuando la función recibe exáctamente un parámetro, los paréntesis pueden ser omitidos, por ejemplo: text => logger.debug(text) es una arrow function válida.

Var, let y const

En adquio se prefiere utilizar let y const para declarar nuevas variables.

Las variables declaradas con let y const solo existen en el bloque que se definen (es decir, lo encerrado por llaves { y }). Las reglas sobre variables definidas con var son más complejas y escapan de la intención de este documento.
La diferencia entre let y const es que las "variables" asignadas con const no pueden cambiar de valor.

const demostracion = (parametro) => {
  if(parametro !== null) {
    const grande = parametro > 1000;
    let pequeno = parametro < 10;
    if(grande) {
      grande = parametro * 1000; // -> Error por reasignar constante
    } else if (pequeno) {
      pequeno = pequeno / 10; // OK
    }
  }
  // grande y pequeno no existen aquí, si se hubiesen definido como var sí existirían en este punto
  logger.debug('grande es', grande); // -> Error: grande is undefined
}

Comparaciones (==, !=, === y !==)

Se recomienda usar siempre los comparadores de tres símbolos (=== y !==). Estos comprueban que el valor sea exáctamente el mismo. Las versiones más laxas != y == intentan convertir tipos de dato para hacer comparaciones flexibles, y pueden resultar en errores inesperados, por ejemplo:

1 === '1' // false
1 == '1' // true
0 === [] // false
0 == [] // true
// Sin embargo... Boolean(0) -> false y Boolean([]) -> true

Manejo de errores (excepciones)

A pesar del nombre, los errores de scripts pueden suceder en el funcionamiento normal del sistema. Por ejemplo, un error sería intentar escribir o leer sobre una variable que se encuentra fuera de servicio. Los errores sirven para notificar al programador que ha ocurrido una situación que habría que contemplar de forma diferente.

Si un error no es manejado (es decir, no se captura y especifica qué hacer en caso de que suceda) el script finalizará prematuramente, sin ejecutar las lineas posteriores al error dentro del mismo bloque.

Los errores no manejados son registrados en el log del script y generan una alerta. De esta forma se puede saber que una circunstancia inesperada no ha sido manejada correctamente.

La forma habitual de manejar errores es poniendo el código que puede lanzar errores en una estructura try...catch:

let lectura;
try {
  // Código que puede generar errores
  lectura = devices.read('variable', 'problematica');
  // Si la línea anterior lanza un error, lo siguiente nunca se ejecutará:
  logger.debug('He podido leer la variable problemática', lectura);
} catch(error) {
  // Código que se ejecuta cuando ocurre un error
  logger.warn('Problema leyendo la variable problematica', error);
}
// Lo que esté fuera de la estructura continúa ejecutándose con normalidad, por eso es importante dejar cualquier variable necesaria en un estado consistente si se quiere seguir operando después de un bloque try...catch

Async/await

Algunos métodos disponibles dentro de los scripts son incompatibles con el uso de async/await (por ejemplo devices.read() o alerts.create()). Por ello, se recomienda no hacer uso de esta característica en los scripts de adquio. Las operaciones habituales no bloquean el proceso de otros scripts como ocurriría en una aplicación JavaScript normal, por lo que no es preciso el uso de Promises o async/await.

Como regla general, el código que sigue a una línea await lanzará un error cuando llame a alguno de los métodos internos de adquio que producen esta restricción.

Valores falsy/truty (ciertos o falsos)

En algunos casos se requieren valores que sean true o false. Sin embargo, por la naturaleza de tipado suave del lenguaje, valores que no sean estríctamente uno de esos dos también son aceptados.

En estos casos, es importante tener claras las equivalencias que va a producir el lenguaje, para evitar dar por "cierto" un valor que no lo es.

En este sentido, sólo los siguientes valores son equivalentes a false:

  • false
  • null
  • undefined (en algunos casos, el valor devuelto al acceder a posiciones o propiedades no existentes. También se puede asignar explícitamente)
  • 0
  • NaN (Not a Number, resultado de realizar operaciones aritméticas sin solución formal)
  • "" o '' (Texto vacío, no incluye textos aparentemente vacíos cómo " " o "\n" que solo contengan espacios, tabuladores o carácteres no visibles)

El resto de valores tomarán valor cierto (equivalen a true), incluyendo los siguientes (sólo por señalar algunos que pueden llevar a confusión):

  • "0" (Texto que contiene una letra cero 0, no confundir con el número 0). Para convertir un número en representación texto a valor number, anteponga un símbolo de suma +, por ejemplo: +"123" devuelve el valor numérico 123
  • {} (Objeto vacío, sin ninguna propiedad)
  • [] (Lista/array vacíos). Para evaluar si una lista está vacía se puede acceder a su propiedad length que devuelve un número entero con el tamaño de la lista
  • " " (Texto con un espacio)
  • "false" (Nuevamente, las cadenas de texto pueden llevar a engaño)