sintaxis - map javascript
¿Qué significan las funciones de flecha múltiple en javascript? (7)
He estado leyendo un montón de código de
react
y veo cosas como esta que no entiendo:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
Comprender las sintaxis disponibles de las funciones de flecha le dará una idea de qué comportamiento están introduciendo cuando están "encadenados" en los ejemplos que proporcionó.
Cuando una función de flecha se escribe sin llaves de bloque, con o sin múltiples parámetros, la expresión que constituye el cuerpo de la función se devuelve implícitamente . En su ejemplo, esa expresión es otra función de flecha.
No arrow funcs Implicitly return `e=>{…}` Explicitly return `e=>{…}`
---------------------------------------------------------------------------------
function (field) { | field => e => { | field => {
return function (e) { | | return e => {
e.preventDefault() | e.preventDefault() | e.preventDefault()
} | | }
} | } | }
Otra ventaja de escribir funciones anónimas utilizando la sintaxis de flecha es que están vinculadas léxicamente al alcance en el que están definidas. Desde ''Funciones de flecha'' en MDN :
Una expresión de función de flecha tiene una sintaxis más corta en comparación con las expresiones de función y enlaza léxicamente this valor. Las funciones de flecha son siempre anonymous .
Esto es particularmente pertinente en su ejemplo teniendo en cuenta que está tomado de una aplicación
reactjs
.
Como lo señaló @naomik, en React a menudo accedes a
las funciones miembro de
un
componente
usando
this
.
Por ejemplo:
Unbound Explicitly bound Implicitly bound
------------------------------------------------------------------------------
function (field) { | function (field) { | field => e => {
return function (e) { | return function (e) { |
this.setState(...) | this.setState(...) | this.setState(...)
} | }.bind(this) |
} | }.bind(this) | }
El ejemplo en su pregunta es el de una
curried function
que utiliza la
arrow function
de
arrow function
y tiene un
implicit return
para el primer argumento.
La función de flecha enlaza esto léxicamente, es decir, no tienen su propio argumento, pero toman el valor de
this
del alcance adjunto
Un equivalente del código anterior sería
const handleChange = (field) {
return function(e) {
e.preventDefault();
/// Do something here
}.bind(this);
}.bind(this);
Una cosa más a tener en cuenta sobre su ejemplo es que define
handleChange
como una constante o una función.
Probablemente lo esté usando como parte de un método de clase y use una
class fields syntax
así que en lugar de vincular la función externa directamente, la vincularía en el constructor de la clase
class Something{
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(field) {
return function(e) {
e.preventDefault();
// do something
}
}
}
Otra cosa a tener en cuenta en el ejemplo es la diferencia entre el retorno implícito y explícito.
const abc = (field) => field * 2;
Arriba hay un ejemplo de retorno implícito, es decir.
toma el campo de valor como argumento y devuelve el
field*2
resultado
field*2
que especifica explícitamente la función a devolver
Para un retorno explícito, le diría explícitamente al método que devuelva el valor
const abc = () => { return field*2; }
Otra cosa a tener en cuenta sobre las funciones de flecha es que no tienen sus propios
arguments
sino que también los heredan del alcance de los padres.
Por ejemplo, si solo define una función de flecha como
const handleChange = () => {
console.log(arguments) // would give an error on running since arguments in undefined
}
Como alternativa, las funciones de flecha proporcionan los parámetros de descanso que puede usar
const handleChange = (...args) => {
console.log(args);
}
Piénselo así, cada vez que ve una flecha, la reemplaza con una
function
.
function parameters
se definen antes de la flecha.
Entonces en tu ejemplo:
field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}
y luego juntos:
function (field) {
return function (e) {
e.preventDefault();
};
}
// Basic syntax:
(param1, param2, paramN) => { statements }
(param1, param2, paramN) => expression
// equivalent to: => { return expression; }
// Parentheses are optional when there''s only one argument:
singleParam => { statements }
singleParam => expression
Puede que no esté totalmente relacionado, pero dado que la pregunta mencionada reacciona usa el caso (y sigo tropezando con este hilo SO): hay un aspecto importante de la función de doble flecha que no se menciona explícitamente aquí. Solo se nombra la ''primera'' flecha (función) (y por lo tanto ''distinguible'' por el tiempo de ejecución), las flechas siguientes son anónimas y desde el punto de vista de React cuentan como un objeto ''nuevo'' en cada render.
Por lo tanto, la función de doble flecha hará que cualquier componente puro se vuelva a procesar todo el tiempo.
Ejemplo
Tiene un componente principal con un controlador de cambios como:
var handleChange = field => e => {
e.preventDefault();
/// Do something here
}
y con un render como:
{ tasks.map(task => <MyTask handleChange={this.handleChange(task)}/> }
handleChange luego se usa en una entrada o haga clic. Y todo esto funciona y se ve muy bien. PERO significa que cualquier cambio que hará que los padres vuelvan a rendirse (como un cambio de estado completamente no relacionado) también volverá a procesar TODA su MyTask, aunque sean Componentes Puros.
Esto puede aliviarse de muchas maneras, como pasar la flecha ''más externa'' y el objeto con el que lo alimentaría o escribir una función de actualización personalizada o volver a lo básico, como escribir funciones con nombre (y vincular esto manualmente ...)
Un consejo general, si se confunde con alguna de las nuevas sintaxis de JS y cómo se compilará, puede verificar babel . Por ejemplo, copiar su código en babel y seleccionar el preajuste es2015 dará una salida como esta
handleChange = function handleChange(field) {
return function (e) {
e.preventDefault();
// Do something here
};
};
Breve y simple 🎈
Es una función que devuelve otra función escrita de forma breve.
const handleChange = field => e => {
e.preventDefault()
// Do something here
}
// is equal to
function handleChange(field) {
return function(e) {
e.preventDefault()
// Do something here
}
}
Por qué la gente lo hace ❓
¿Te has enfrentado cuando necesitas escribir una función que se puede personalizar? ¿O tiene que escribir una función de devolución de llamada que tenga parámetros fijos (argumentos), pero necesita pasar más variables a la función pero evitando las variables globales? Si su respuesta es " sí ", entonces es la forma de hacerlo.
Por ejemplo, tenemos un
button
con devolución de llamada onClick.
Y necesitamos pasar la
id
a la función, pero
onClick
acepta solo un
event
parámetro, no podemos pasar parámetros adicionales dentro de este modo:
const handleClick = (event, id) {
event.preventDefault()
// Dispatch some delete action by passing record id
}
¡No funcionará!
Por lo tanto, hacemos una función que devolverá otra función con su propio alcance de variables sin ninguna variable global, porque las variables globales son malvadas 😈.
Debajo de la función
handleClick(props.id)}
se llamará y devolverá una función y tendrá
id
en su alcance.
No importa cuántas veces se presione, los identificadores no se afectarán ni cambiarán entre sí, están totalmente aislados.
const handleClick = id => event {
event.preventDefault()
// Dispatch some delete action by passing record id
}
const Confirm = props => (
<div>
<h1>Are you sure to delete?</h1>
<button onClick={handleClick(props.id)}>
Delete
</button>
</div
)
Esa es una función curry
Primero, examine esta función con dos parámetros ...
const add = (x, y) => x + y
add(2, 3) //=> 5
Aquí está de nuevo en forma de curry ...
const add = x => y => x + y
Aquí está el mismo código 1 sin funciones de flecha ...
const add = function (x) {
return function (y) {
return x + y
}
}
Centrarse en el
return
Puede ser útil visualizarlo de otra manera. Sabemos que las funciones de flecha funcionan así: prestemos especial atención al valor de retorno .
const f = someParam => returnValue
Entonces, nuestra función
add
devuelve una
función
: podemos usar paréntesis para mayor claridad.
El texto en
negrita
es el valor de retorno de nuestra función
add
const add = x => (y => x + y)
En otras palabras,
add
un número devuelve una función
add(2) // returns (y => 2 + y)
Llamar a funciones curry
Entonces, para usar nuestra función de curry, tenemos que llamarlo un poco diferente ...
add(2)(3) // returns 5
Esto se debe a que la primera llamada de función (externa) devuelve una segunda función (interna). Solo después de llamar a la segunda función, realmente obtenemos el resultado. Esto es más evidente si separamos las llamadas en dos líneas ...
const add2 = add(2) // returns function(y) { return 2 + y }
add2(3) // returns 5
Aplicando nuestra nueva comprensión a su código
relacionado: "¿Cuál es la diferencia entre encuadernación, aplicación parcial y curry?"
Bien, ahora que entendemos cómo funciona, veamos tu código
handleChange = field => e => {
e.preventDefault()
/// Do something here
}
Comenzaremos por representarlo sin usar las funciones de flecha ...
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
};
};
Sin embargo, debido a que las funciones de flecha unen léxicamente
this
, en
realidad se
vería más así ...
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
}.bind(this)
}.bind(this)
Quizás ahora podamos ver lo que esto está haciendo más claramente.
La función
handleChange
está creando una función para un
field
específico.
Esta es una técnica práctica de React porque debes configurar tus propios oyentes en cada entrada para actualizar el estado de tus aplicaciones.
Al usar la función
handleChange
, podemos eliminar todo el código duplicado que resultaría en la configuración de escuchas de
change
para cada campo.
¡Bueno!
1
Aquí no tuve que vincular
this
léxicamente porque la función de
add
original no usa ningún contexto, por lo que no es importante preservarla en este caso.
Aún más flechas
Se pueden secuenciar más de dos funciones de flecha, si es necesario:
const three = a => b => c =>
a + b + c
const four = a => b => c => d =>
a + b + c + d
three (1) (2) (3) // 6
four (1) (2) (3) (4) // 10
Las funciones curry son capaces de sorprender.
A continuación, vemos
$
definido como una función currificada con dos parámetros, pero en el sitio de la llamada, parece que podemos proporcionar cualquier número de argumentos.
El curry es la abstracción de la
arity
.
const $ = x => k =>
$ (k (x))
const add = x => y =>
x + y
const mult = x => y =>
x * y
$ (1) // 1
(add (2)) // + 2 = 3
(mult (6)) // * 6 = 18
(console.log) // 18
$ (7) // 7
(add (1)) // + 1 = 8
(mult (8)) // * 8 = 64
(mult (2)) // * 2 = 128
(mult (2)) // * 2 = 256
(console.log) // 256
Aplicación parcial
La aplicación parcial es un concepto relacionado. Nos permite aplicar parcialmente funciones, similares al currículum, excepto que la función no tiene que definirse en forma de currículum.
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const add3 = (x, y, z) =>
x + y + z
partial (add3) (1, 2, 3) // 6
partial (add3, 1) (2, 3) // 6
partial (add3, 1, 2) (3) // 6
partial (add3, 1, 2, 3) () // 6
partial (add3, 1, 1, 1, 1) (1, 1, 1, 1, 1) // 3
Aquí hay una demostración funcional de
partial
que puedes jugar en tu propio navegador:
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const preventDefault = (f, event) =>
( event .preventDefault ()
, f (event)
)
const logKeypress = event =>
console .log (event.which)
document
.querySelector (''input[name=foo]'')
.addEventListener (''keydown'', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">