Cómo mejorar su código con nombres de funciones que revelan la intención

¡Descubrir JavaScript funcional fue nombrado uno de los mejores libros nuevos de programación funcional por BookAuthority!

El código es una forma de comunicarse con los desarrolladores que lo leen. Las funciones con nombres reveladores de intención son más fáciles de leer. Leemos el nombre de la función y podemos entender su propósito. El nombre de la función es nuestra herramienta para expresar la intención en un fragmento de código.

Veamos una lista de operaciones realizadas en un estilo funcional con el uso de funciones anónimas.

función getTodos (usuarios) {
  volver todos
    .filter (todo =>! todo.completed && todo.type === "RE")
    .map (todo => ({
      título: todo.title,
      userName: users [todo.userId] .name
    }))
    .sort ((todo1, todo2) =>
      todo1.userName.localeCompare (todo2.userName));
}

Ahora verifique la misma funcionalidad refactorizada usando funciones con nombres reveladores de intención.

función isTopPriority (todo) {
  return! todo.completed && todo.type === "RE";
}
función ascByUserName (todo1, todo2) {
  devolver todo1.userName.localeCompare (todo2.userName);
}
  
función getTodos (usuarios) {
  función toViewModel (todo) {
    regreso {
      título: todo.title,
      userName: users [todo.userId] .name
    }
  }
  return todos.filter (isTopPriority)
              .map (toViewModel) .sort (ascByUserName);
}

Los nombres de funciones dan claridad al código. Con un buen nombre de función, solo tiene que leer el nombre; no necesita analizar su código para comprender lo que hace.

Se estima ampliamente que los desarrolladores dedican el 70% del tiempo de mantenimiento del código a leer para comprenderlo.
Kyle Simpson en JavaScript con luz funcional

Funciones puras y cierres

Si lo piensa, podemos dividir las funciones anidadas en dos: cierres y funciones puras.

Las funciones puras no usan variables de las funciones externas. Además, devuelven un valor y no tienen efectos secundarios.

Las funciones de cierre utilizan variables de las funciones externas.

Función pura

Una función pura es el mejor de los casos. Se puede refactorizar y se le puede dar un nombre que revele intenciones fácilmente.

isTopPriority () y ascByUserName () son funciones puras.

Cierre

Una función de cierre es más problemática. Puede hacer referencia a variables de diferentes funciones externas. Podemos comenzar refactorizándolo a una función con nombre, pero manténgalo en la misma función principal.

toViewModel () es un cierre.

Si desea leer más sobre el cierre, eche un vistazo a Por qué debería darle otra oportunidad a la función de Cierre.

Refactorización de devoluciones de llamadas anónimas a funciones con nombre

Una devolución de llamada es una función pasada como argumento a otra función

Promesa con devolución de llamada anónima

Tomaré el caso de una promesa con una devolución de llamada anónima.

función fetch () {
  return dataService.fetch (). then (newTodos => {
    todos = newTodos;
    eventEmitter.fire ();
  });
}

A continuación se muestra el código después de refactorizar a una función con un nombre que revela la intención.

función setLocalTodos (newTodos) {
  todos = newTodos;
  eventEmitter.fire ();
}
función fetch () {
  return dataService.fetch (). then (setLocalTodos);
}

Evento con devolución de llamada anónima

Ahora mire el caso de un controlador de eventos escrito como una función anónima.

$ ("# first"). haga clic en (() => {
  var list = getItemsFromList ();
  var filterLIst = list.slice (0,1);
  renderList (filterLIst);
});

Y así es como se ve el código después de refactorizar a una declaración de función con un nombre claro.

$ ("# first"). clic (renderFirstItem);
función renderFirstItem () {
  var list = getItemsFromList ();
  var filterLIst = list.slice (0,1);
  renderList (filterLIst);
}

La declaración de función define una función con nombre. La línea de código comienza con la función.

No utilice nombres de funciones que no agreguen ningún valor adicional al código existente. Por ejemplo, no use nombres como onClick () o myCustomClickEvent (). El nombre debe expresar lo que hace el código, y un nombre básico puede usarse en otros lugares.

Usar una expresión de función con nombre

Ahora veamos cómo mejorar la legibilidad del código utilizando un IIFE anónimo.

IIFE significa Expresión de función invocada inmediatamente. Es una expresión de función que se ejecuta inmediatamente después de su definición.

(función(){
  "uso estricto";
  let $ form = $ ("# editForm");
  dejar personas = [];
$ ("# guardar", $ formulario) .on ("clic", () => {
      dejar persona = {
        fname: $ ("# fname", $ form) .val (),
        lname: $ ("# lname", $ form) .val ()
      };
      persons.push (persona);
  });
}) ();

Esta vez, solo daré un nombre revelador a la función anónima.

(función renderPersonEditForm () {
  "uso estricto";
  let $ form = $ ("# editForm");
  dejar personas = [];
$ ("# save", $ form) .on ("click", función savePerson () {
      dejar persona = {
        fname: $ ("# fname", $ form) .val (),
        lname: $ ("# lname", $ form) .val ()
      };
      persons.push (persona);
  });
}) ();

renderPersonEditForm () y savePerson () se denominan expresiones de función.

La expresión de función define una función como parte de una expresión más grande. Una expresión de función puede ser nombrada o anónima. La línea de código, en este caso, no comienza con la función.

Refactorizar a funciones nombradas también mejorará la experiencia de depuración.

Mejorando la experiencia de depuración

A continuación, exageraré la situación y usaré tres IIFE anónimos para hacer el punto.

(función(){
    /*código*/
    (función(){
        /*código*/
        (función(){
            /*código*/
        }) ();
    }) ();
}) ();

A continuación podemos consultar la Pila de llamadas. Todo lo que podemos ver son funciones anónimas.

Expresiones de funciones anónimas en la pila de llamadas

Las funciones anónimas aparecerán como (anónimas) en CallStack

Ahora, mire el código escrito con expresiones de funciones con nombre.

(función createTodoModule () {
    /*código*/
    (función getAllTodos () {
        /*código*/
        (función renderTodo () {
            /*código*/
        }) ();
    }) ();
}) ();

Esta vez, la Pila de llamadas es más fácil de entender.

Expresiones de funciones de nombre en la pila de llamadas

Usando la función de flecha

Creo que dar un nombre también puede mejorar la legibilidad en casos con poca lógica. Tomemos el ejemplo de calcular una suma sobre una matriz.

// usando la función anónima
let total = numbers.reduce ((a, b) => a + b, 0);
// usando la declaración de función
función sum (a, b) {devuelve a + b; }
let total = numbers.reduce (suma, 0);

Como puede ver, refactoré la función de flecha anónima a una declaración de función, ya que me resulta más fácil de leer.

Si prefiere usar la función de flecha, puede dar el nombre de la función usando una variable.

let sum = (a, b) => a + b;
let total = numbers.reduce (suma, 0);

A veces, el código es tan pequeño que el nombre de la función no agrega ningún valor. En el siguiente caso, nos quedamos con la función de flecha anónima hasta que los códigos se vuelvan más complejos. Solo en ese punto, lo refactorizaremos a una declaración de función.

let completeTodos = todos.filter (todo => todo.completed);

Conclusión

Utilizo funciones con nombre, en lugar de anónimas, por tres razones principales:

  • legibilidad
  • mejor experiencia de depuración
  • la opción de autorreferencia, que será utilizada por funciones recurrentes

Los nombres de funciones que expresan la intención los hacen mucho más fáciles de entender y facilitan el cambio del código.

¡Descubrir JavaScript funcional fue nombrado uno de los mejores libros nuevos de programación funcional por BookAuthority!

Para obtener más información sobre la aplicación de técnicas de programación funcional en React, eche un vistazo a Functional React.

Lea más sobre Vue y Vuex en Una introducción rápida a los componentes de Vue.js.

Aprenda a aplicar los Principios de los patrones de diseño.

Puedes encontrarme también en Twitter.