lunes, 26 de agosto de 2013

JavaScript: diferencia entre expresión y sentencia ( expression and statement)

En JavaScript es importante conocer esta sutil diferencia porque es la causa de algunos comportamientos "especiales".

-Una expresión siempre devuelve un valor como resultado
-Una sentencia realiza alguna acción

Ejemplos clásicos de sentencias son las instrucciones 'if', 'for', 'switch' o 'while':


if ( limit > 3) {
    limit = 0;
}


for ( var i=0; i<10; i++ ) {
    // do something here
}

Todos los siguientes ejemplos son expresiones, todos devuelven un valor:


a = 3      // es una asignación pero también devuelve el valor 3
3 + 2      // devuelve 5
myCounter  // devuelve el valor de la variable 'myCounter'
"abc"      // devuelve el string "abc"
sum(a,b)   // devuelve la suma de a y b
(limit > 3) ? 0 : 1;   // devuelve 0 ó 1, según condición 

Cuando creamos un objeto con la notación literal, estamos realmente utilizando una expresión que devuelve un objeto:

{ color: "red" }    // esto es una expresión que devuelve un objeto
[1, 2, 3]           // expresión que devuelve un array  
"abc"               // expresión que devuelve un string
function () { }     // function anónima. Expresión que devuelve una función

En JavaScript podemos utilizar una expresión en cualquier punto donde se espere un valor, por ejemplo como argumento de una función o en un switch:

//como argumento
myFunction( a+3, b);

//en un switch
switch (expression) {
  case label1:
    //..
    break
  case label2:
    //..
    break;
  default:
    //..
}

JavaScript acepta también una expresión en cualquier punto donde se espera una sentencia. Lo contrario no es cierto, no podemos escribir una sentencia donde se espera una expresión. Por ejemplo, no podemos poner un 'if' como argumento de una función.

declaración de función vs expresión de función

Una función puede crearse mediate una sentencia (declaración) o mediante una expresión. Lo siguiente es una declaración de función:

function suma (a, b) {
  return a+b;
}

Como sentencia, realiza una acción, crea una variable suma que contiene la función.

Una expresión devolverá un valor (una función) que podemos asignar a una variable:

var suma = function ( a, b ) {
               return a+b;
            }

También podemos tener una expresión que crea una función con nombre (named function expression):

var sumVar = function suma ( a, b ) {
               return a+b;
            };

En este caso la parte a la derecha de la función es exactamente igual que una declaración de función, pero no es una declaración, es una expresión. ¿Como sabemos entonces si es una declaración (sentencia) o una expresión? por el contexto en el que aparece.

Cuando el parser de JavaScript encuentra la palabra reservada function al principio de una instrucción, lo toma como una declaración de función. Cuando la encuentra formando parte de una sentencia (o de una expresión actuando como sentencia), lo considera una expresión (por ejemplo en una asignación).

Es importante el concepto de contexto de expresión (expression context) y de contexto de sentencia (statement context). Debemos tenerlo en cuenta siempre que un mismo código pueda actuar como expresión y como sentencia y debemos saber como forzar el cambio de interpretación si lo necesitamos.

Un ejemplo de cambio de contexto son las funciones autoejecutables. JavaScript no permite ejecutar una declaración de función, por lo que no podemos hacer esto:

//ejemplo de error. No se puede ejecutar
function suma (a, b) {
  return a+b;
}();

Sólo una expresión de función puede ejecutarse. Para que el intérprete considere la función como una expresión tenemos que colocarla entre paréntesis. De esta forma la palabra function no se encuentra al principio y el motor de JS lo considera como una expresión a evaluar.

(function ( a, b ) {
    return a+b;
})();

Nota: '(...)' es el operador de agrupado y sólo puede contener una expresión. Por lo tanto JavaScript siempre esperará una expresión entre los paréntesis. Si intentamos colocar una sentencia dentro de los parentesis obtendremos un syntax error.

Otro caso parecido, en el que el contexto decide como se interpreta un código idéntico, es la notación literal para crear un objeto:

{ 
  color: "red" 
}

Este código puede interpretarse de dos maneras:

  1. una expresión que crea un objeto
  2. Un bloque de código ( '{ }' ) que contiene una etiqueta ('color:') y una expresión que actúa como sentencia ("red")

Igual que en el caso de la función, si el parser encuentra '{' en el inicio, sin formar parte de una sentencia o otra expresión, lo considera como el inicio de un bloque y no como una expresión; no creará el objeto. Ademas, si tenemos varias propiedades provocará un syntax error: Unexpected token:

//Syntax error
{ 
  color: "red",
  size: 14 
}

Si lo encerramos entre paréntesis o lo asignamos a una variable, el intérprete lo considera una expresión y crea el objeto. Esta es la razón por la que tenemos que añadir paréntesis a un string JSON si queremos evaluarlo con eval():

eval ("(" + myJsonString + ")" );

Fuentes:
Expressions versus statements in JavaScript
Named function expressions demystified

No hay comentarios:

Publicar un comentario