Cómo comenzar rápidamente con VUE3.0: Ingrese a
las recomendaciones relacionadas con el aprendizaje: Tutorial de aprendizaje de JavaScript.
He visto muchas explicaciones sobre programación funcional, pero la mayoría de ellas son de nivel teórico y algunas son solo para lenguajes de programación funcionales puros. como Haskell. El propósito de este artículo es hablar sobre la práctica específica de la programación funcional en JavaScript en mi opinión. La razón por la que está "en mi opinión" significa que lo que digo solo representa mi opinión personal, que puede entrar en conflicto con algunos conceptos estrictos.
Este artículo omitirá gran parte de la introducción formal de conceptos y se centrará en mostrar qué es el código funcional en JavaScript, cuál es la diferencia entre el código funcional y la escritura general, qué beneficios puede brindarnos el código funcional y cuáles son algunos modelos funcionales comunes.
Creo que la programación funcional puede entenderse como un método de programación que utiliza funciones como portador principal.
¿Cuáles son los beneficios de hacerlo? Los puntos principales son los siguientes:
semántica más clara, mayor reutilización, mayor mantenibilidad, mejor limitación del alcance y menos efectos secundarios. Programación funcional básica. El siguiente ejemplo es una expresión funcional específica del
código Javascript
// cada palabra en la matriz. primera letra // Escritura general const arr = ['apple', 'pen', 'apple-pen'] for(const i in arr){ constante c = arreglo[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1); consola.log(arr); // Método de escritura funcional - función lowerFirst(palabra) { devolver palabra[0].toUpperCase() + palabra.slice(1); función palabraToUpperCase(arr) { devolver arr.map(superiorPrimero); console.log(wordToUpperCase(['apple', 'pen', 'apple-pen'])); // Método de escritura funcional 2 console.log(arr.map(['apple', 'pen', 'apple-pen'], word => word[0].toUpperCase() + word.slice(1)))
Cuando la situación se vuelve más compleja, la forma de escribir expresiones encontrará varios problemas:
el significado no es obvio, gradualmentese
vuelve difícil de mantener, la reutilización es pobre, se generará más código y se generarán muchas variables intermedias; La programación funcional resolvió bien los problemas anteriores. Primero, consulte el método de escritura funcional 1, que utiliza la encapsulación de funciones para descomponer funciones (la granularidad no es única), las encapsula en diferentes funciones y luego usa llamadas combinadas para lograr el propósito. Esto hace que la expresión sea clara y fácil de mantener, reutilizar y ampliar. En segundo lugar, al utilizar funciones de orden superior, Array.map reemplaza for...of para el recorrido de la matriz, lo que reduce las variables y operaciones intermedias.
La principal diferencia entre el método de escritura funcional 1 y el método de escritura funcional 2 es que puede considerar si la función se puede reutilizar más adelante. De lo contrario, este último es mejor.
Del método de escritura funcional 2 anterior, podemos ver que en el proceso de escribir código funcional, es fácil provocar una extensión horizontal, es decir, producir múltiples capas de anidamiento. A continuación se muestra un ejemplo extremo.
Código Javascript
// Calcular la suma de números // Método de escritura general console.log(1 + 2 + 3 - 4) // Función de escritura funcional suma(a, b) { devolver a + b } función sub(a, b) { devolver a-b; console.log(sub(sum(sum(1, 2), 3), 4); este ejemplo solo muestra un caso extremo de extensión horizontal. A medida que el número de niveles anidados de funciones continúa aumentando, el código se volverá menos legible El rendimiento se reduce considerablemente y es fácil cometer errores. En este caso, podemos considerar múltiples métodos de optimización, como la siguiente optimización en cadena. // Optimizar la escritura (bueno, leíste bien, esta es la escritura en cadena de lodash) Código Javascript const utils = { cadena (a) { this._temp = a; devolver esto; }, suma(b) { this._temp += b; devolver esto; }, sub(b) { this._temp -= b; devolver esto; }, valor() { const _temp = this._temp; this._temp = indefinido; devolver _temp; } }; console.log(utils.chain(1).sum(2).sum(3).sub(4).value());
después de reescribir de esta manera, la estructura general se volverá más clara y cada eslabón de la cadena. Será Qué hacer también se puede mostrar fácilmente. Otro buen ejemplo de la comparación entre anidamiento y encadenamiento de funciones es la función de devolución de llamada y el patrón Promesa.
Código Javascript
// Solicitar dos interfaces secuencialmente // Función de devolución de llamada import $ from 'jquery' $.post('a/url/to/target', (rs) => { si(rs){ $.post('una/url/a/otro/destino', (rs2) => { si(rs2){ $.post('a/url/a/tercero/destino'); } }); } }); // Solicitud de importación de promesa de 'catta'; // catta es una herramienta de solicitud liviana que admite fetch, jsonp, ajax y no tiene dependencias request('a/url/to/target') .then(rs => rs ? $.post('a/url/a/otro/objetivo'): Promise.reject()) .then(rs2 => rs2 ? $.post('a/url/to/third/target') : Promise.reject());
A medida que aumente el nivel de anidamiento de la función de devolución de llamada y la complejidad de un solo nivel, se convertirá en Es. Inflado y difícil de mantener, pero la estructura de la cadena de Promise aún puede expandirse verticalmente cuando la complejidad es alta y el aislamiento jerárquico es muy claro.
El cierre del modelo de programación funcional común
puede retener variables locales en un bloque de código que no se publica. Se llama cierre.
Creo que todo el mundo sabe más o menos
qué beneficios tiene. ¿Pueden traernos bolsas?
Primero veamos cómo crear un cierre:
Código Javascript
// Crear una función de cierre makeCounter() { sea k = 0; función de retorno() { devolver ++k; }; contador constante = makeCounter(); console.log(counter()); // 1 console.log(counter()); // 2
makeCounter El bloque de código de esta función hace referencia a la variable local k en la función devuelta, lo que provoca que la variable local falle. Cuando se ejecuta la función, es reciclada por el sistema generando así un cierre. La función de este cierre es "retener" la variable local para que pueda reutilizarse cuando se llama a la función interna; a diferencia de las variables globales, solo se puede hacer referencia a esta variable dentro de la función;
En otras palabras, los cierres en realidad crean algunas "variables persistentes" que son privadas de la función.
Entonces, de este ejemplo, podemos concluir que las condiciones para crear un cierre son:
hay funciones internas y externas. La función interna se refiere a las variables locales de la función externa. El propósito principal del cierre es definir. algunas funciones. Variables persistentes limitadas por dominio, estas variables se pueden utilizar para almacenamiento en caché o cálculos intermedios, etc.
Código Javascript
// Herramienta de almacenamiento en caché simple // La función anónima crea un cierre const cache = (function() { tienda constante = {}; devolver { obtener (clave) { devolver tienda[clave]; }, establecer (clave, val) { almacenar[clave] = val; } } }()); cache.set('a', 1); cache.get('a'); // 1El
ejemplo anterior es la implementación de una herramienta de almacenamiento en caché simple. La función anónima crea un cierre para que siempre se pueda hacer referencia al objeto de la tienda. , no será reciclado.
Desventajas de los cierres: las variables persistentes no se liberarán normalmente y seguirán ocupando espacio en la memoria, lo que fácilmente provocará un desperdicio de memoria, por lo que generalmente se requieren algunos mecanismos de limpieza manual adicionales.
función que acepta o devuelve una función se llama función de orden superior.
Suena como una palabra muy fría, pero en realidad la usamos a menudo, pero simplemente no sabemos sus nombres. El lenguaje JavaScript admite de forma nativa funciones de orden superior, porque las funciones de JavaScript son ciudadanas de primera clase y pueden usarse como parámetros y como valor de retorno de otra función.
A menudo podemos ver muchas funciones nativas de alto orden en JavaScript, como Array.map, Array.reduce y Array.filter.
Tomemos
map como ejemplo. Veamos cómo usa
En otras palabras, cada elemento del conjunto se somete a la misma transformación para generar un nuevo
mapa de conjunto. Como función de alto orden, acepta un parámetro de función como el
código Javascript
lógico del mapeo// Agrega uno a cada elemento del conjunto. la matriz para formar una nueva matriz // Método de escritura general const arr = [1,2,3]; const rs = []; rs.push(++n); } consola.log(rs) // reescribe el mapa const arr = [1,2,3]; const rs = arr.map(n => ++n);
el uso del bucle for...of para atravesar la matriz causará operaciones adicionales, y existe el riesgo de cambiar la matriz original
, pero la función de mapa encapsula las operaciones necesarias, por lo que solo debemos preocuparnos por la implementación de la función de la lógica de mapeo, lo que reduce la cantidad de código y el riesgo de errores secundarios. efectos.
proporciona algunos parámetros de una función y genera una nueva función que acepta otros parámetros.
Puede que no escuches este término con frecuencia, pero cualquiera que haya usado undescore o lodash lo ha visto.
Hay una función mágica _.partial, que es
código Javascript
al curry// Obtener la ruta relativa del archivo de destino a la ruta base // El método de escritura general es const BASE = '/path/to/base'; ruta.relativa(BASE, '/alguna/ruta'); // _.parical reescribir const BASE = '/ruta/a/base'; const relativaDesdeBase = _.partial(ruta.relativa, BASE); const relativaPath = relativaFromBase('/some/path');
A través de _.partial, obtenemos la nueva función relativaFromBase. Cuando se llama a esta función, es equivalente a llamar a path.relative, y el primer parámetro se pasa a BASE de forma predeterminada. Los parámetros posteriores ingresados se agregan en orden.
En este caso, lo que realmente queremos lograr es obtener la ruta relativa a BASE cada vez, no relativa a ninguna ruta. El curry nos permite preocuparnos solo por algunos de los parámetros de una función, lo que aclara el propósito de la función y la llama más simple.
combina las capacidades de múltiples funciones para crear una nueva función.
Es posible que lo haya visto por primera vez en lodash, el método de composición (ahora llamado flujo)
Código Javascript
// Escriba en mayúscula cada palabra en la matriz, use Base64. //Método de escritura general (uno de ellos) const arr = ['pen', 'apple', 'applypen']; rs.push(btoa(w.toUpperCase())); } console.log(rs); // _.flow reescritura const arr = ['pen', 'apple', 'applypen']; const UpperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa)); console.log(upperAndBase64(arr));
_.flow combina las capacidades de las funciones de conversión de mayúsculas y de conversión Base64 para generar una nueva función. Conveniente para usar como función de parámetro o reutilización posterior.
Desde mi propio punto de vista, mi comprensión de la programación funcional de JavaScript puede ser diferente de muchos conceptos tradicionales. No creo que solo las funciones de alto orden cuenten como programación funcional. Otras, como las llamadas combinadas a funciones ordinarias, las estructuras en cadena, etc., creo que pertenecen a la categoría de programación funcional, siempre que utilicen funciones como principales. transportador.
Y creo que la programación funcional no es necesaria ni debería ser una regla o requisito obligatorio. Al igual que las ideas orientadas a objetos u otras, también es una de las formas. En la mayoría de los casos, deberíamos ser una combinación de varios, en lugar de limitarnos a conceptos.
Recomendaciones relacionadas: Tutorial de JavaScript
Lo anterior es una discusión detallada sobre la programación funcional de JavaScript. Para obtener más información, preste atención a otros artículos relacionados en el sitio web chino de PHP.