sintaxis - map javascript
¿Cuándo debo usar las funciones de flecha en ECMAScript 6? (7)
Funciones de flecha: la característica más utilizada de ES6 hasta ahora ...
Uso: Todas las funciones de ES5 deben reemplazarse con las funciones de flecha de ES6, excepto en los siguientes escenarios:
Las funciones de flecha NO deben ser usadas:
- Cuando queremos función de elevación.
- Como las funciones de flecha son anónimas.
- Cuando queremos usar
this
/arguments
en una función.- Como las funciones de flecha no tienen
this
/arguments
propios, dependen de su contexto externo.
- Como las funciones de flecha no tienen
- Cuando queremos usar la función nombrada.
- Como las funciones de flecha son anónimas.
- Cuando queremos usar la función como
constructor
- Como las funciones de flecha no tienen su propio
this
.
- Como las funciones de flecha no tienen su propio
- Cuando queremos agregar la función como una propiedad en el literal del objeto y usar el objeto en ella
- Como no podemos acceder a
this
(que debería ser objeto en sí).
- Como no podemos acceder a
Entendamos algunas de las variantes de las funciones de flecha para entender mejor:
Variante 1 : cuando queremos pasar más de un argumento a una función y devolverle algún valor.
Versión ES5 :
var multiply = function (a,b) {
return a*b;
};
console.log(multiply(5,6)); //30
Versión ES6 :
var multiplyArrow = (a,b) => a*b;
console.log(multiplyArrow(5,6)); //30
Nota: la palabra clave de function
NO es obligatoria. =>
se requiere. {}
son opcionales, cuando no proporcionamos la return
{}
se agrega implícitamente por JavaScript y cuando proporcionamos {}
necesitamos agregar la return
si la necesitamos.
Variante 2 : cuando queremos pasar SÓLO un argumento a una función y devolverle algún valor.
Versión ES5 :
var double = function(a) {
return a*2;
};
console.log(double(2)); //4
Versión ES6 :
var doubleArrow = a => a*2;
console.log(doubleArrow(2)); //4
Nota: Al pasar solo un argumento podemos omitir paréntesis ()
.
Variante 3 : cuando NO queremos pasar ningún argumento a una función y NO queremos devolver ningún valor.
Versión ES5 :
var sayHello = function() {
console.log("Hello");
};
sayHello(); //Hello
Versión ES6 :
var sayHelloArrow = () => {console.log("sayHelloArrow");}
sayHelloArrow(); //sayHelloArrow
Variante 4 : Cuando queremos regresar explícitamente de las funciones de flecha.
Versión ES6 :
var increment = x => {
return x + 1;
};
console.log(increment(1)); //2
Variante 5 : Cuando queremos devolver un objeto desde las funciones de flecha.
Versión ES6 :
var returnObject = () => ({a:5});
console.log(returnObject());
Nota: Necesitamos envolver el objeto entre paréntesis ()
contrario, JavaScript no puede diferenciar entre un bloque y un objeto.
Variante 6 : Las funciones de flecha NO tienen arguments
(una matriz como objeto) propios, dependen del contexto externo para los arguments
.
Versión ES6 :
function foo() {
var abc = i => arguments[0];
console.log(abc(1));
};
foo(2); // 2
Nota: foo
es una función ES5, con una matriz de arguments
como un objeto y un argumento que se le pasa es 2
por lo que los arguments[0]
para foo
son 2.
abc
es una función de flecha ES6 ya que NO tiene sus propios arguments
por lo tanto, imprime los arguments[0]
de foo
en su contexto externo.
Variante 7 : Las funciones de flecha NO tienen this
propio, dependen del contexto externo para this
Versión ES5 :
var obj5 = {
greet: "Hi, Welcome ",
greetUser : function(user) {
setTimeout(function(){
console.log(this.greet + ": " + user); // "this" here is undefined.
});
}
};
obj5.greetUser("Katty"); //undefined: Katty
Nota: la devolución de llamada pasada a setTimeout es una función de ES5 y tiene su propia función, que no está definida en use-strict
entorno de use-strict
, por lo que obtenemos resultados:
undefined: Katty
Versión ES6 :
var obj6 = {
greet: "Hi, Welcome ",
greetUser : function(user) {
setTimeout(() => console.log(this.greet + ": " + user));
// this here refers to outer context
}
};
obj6.greetUser("Katty"); //Hi, Welcome: Katty
Nota: La devolución de llamada pasada a setTimeout
es una función de flecha de ES6 y NO tiene su propia función, por lo que la toma de su contexto externo, que es greetUser
que tiene this
que es obj6
por lo que obtenemos resultados:
Hi, Welcome: Katty
Misceláneo: No podemos usar new
funciones de flecha.Las funciones de flecha no tienen prototype
propiedad. NO tenemos vinculación de this
cuando la función de flecha se invoca a través de apply
o call
.
La pregunta está dirigida a las personas que han pensado en el estilo de código en el contexto del próximo ECMAScript 6 (Harmony) y que ya han trabajado con el idioma.
Con () => {}
y function () {}
estamos obteniendo dos formas muy similares de escribir funciones en ES6. En otros idiomas, las funciones lambda a menudo se distinguen por ser anónimas, pero en ECMAScript cualquier función puede ser anónima. Cada uno de los dos tipos tiene dominios de uso únicos (es decir, cuando es necesario vincularlo explícitamente o no vincularlo explícitamente). Entre esos dominios hay una gran cantidad de casos en los que cualquier notación servirá.
Las funciones de flecha en ES6 tienen al menos dos limitaciones:
- No trabajar con
new
- Se corrigió
this
límite de alcance en la inicialización.
Dejando de lado estas dos limitaciones, las funciones de flecha podrían, en teoría, reemplazar las funciones regulares en casi cualquier lugar. ¿Cuál es el enfoque correcto usándolos en la práctica? Deben usarse las funciones de flecha, por ejemplo:
- "en todas partes funcionan", es decir, en todas partes, una función no tiene que ser agnóstica acerca de
this
variable y no estamos creando un objeto. - solo "donde sea que se necesiten", es decir, escuchas de eventos, tiempos de espera, que deben estar vinculados a un cierto alcance
- con funciones ''cortas'' pero no con funciones ''largas''
- Solo con funciones que no contengan otra función de flecha.
Lo que estoy buscando es una guía para seleccionar la notación de función adecuada en la versión futura de ECMAScript. La directriz deberá ser clara, para que pueda ser enseñada a los desarrolladores en un equipo, y ser coherente para que no requiera refactorización constante de una notación de función a otra.
De acuerdo con la proposal , las flechas apuntaban a "abordar y resolver varios puntos comunes de la Function Expression
tradicional de la Function Expression
". Pretendían mejorar las cosas vinculándolas léxicamente y ofreciendo una sintaxis concisa.
Sin embargo,
- Uno no puede enlazar consistentemente
this
léxico - La sintaxis de la función de flecha es delicada y ambigua.
Por lo tanto, las funciones de flecha crean oportunidades de confusión y errores, y deben ser excluidas del vocabulario de un programador de JavaScript, reemplazadas con la function
exclusivamente.
En cuanto a this
lexico
this
es problemático
function Book(settings) {
this.settings = settings;
this.pages = this.createPages();
}
Book.prototype.render = function () {
this.pages.forEach(function (page) {
page.draw(this.settings);
}, this);
};
Las funciones de flecha intentan solucionar el problema donde necesitamos acceder a una propiedad de this
dentro de una devolución de llamada. Ya hay varias formas de hacerlo: se podría asignar this
a una variable, usar bind
o usar el tercer argumento disponible en los métodos agregados de Array
. Sin embargo, las flechas parecen ser la solución más sencilla, por lo que el método se podría reformular de la siguiente manera:
this.pages.forEach(page => page.draw(this.settings));
Sin embargo, considere si el código utiliza una biblioteca como jQuery, cuyos métodos vinculan this
especialmente. Ahora, hay dos valores con los que lidiar:
Book.prototype.render = function () {
var book = this;
this.$pages.each(function (index) {
var $page = $(this);
book.draw(book.currentPage + index, $page);
});
};
Debemos usar la function
para que each
pueda enlazar this
dinámicamente. No podemos usar una función de flecha aquí.
Tratar con varios de this
valores también puede ser confuso, porque es difícil saber de qué habla this
autor:
function Reader() {
this.book.on(''change'', function () {
this.reformat();
});
}
¿El autor realmente Book.prototype.reformat
llamar Book.prototype.reformat
? ¿O se olvidó de enlazar this
y tenía la intención de llamar a Reader.prototype.reformat
? Si cambiamos el controlador a una función de flecha, nos preguntaremos de forma similar si el autor deseaba this
dinámica, pero eligió una flecha porque encajaba en una línea:
function Reader() {
this.book.on(''change'', () => this.reformat());
}
Uno puede plantear: "¿Es excepcional que las flechas a veces sean la función incorrecta de usar? Tal vez si raramente necesitamos this
valores dinámicos, todavía estaría bien usar flechas la mayor parte del tiempo".
Pero pregúntese lo siguiente: "¿Valdría la pena ''depurar'' el código y descubrir que un ''caso extremo'' provocó el resultado de un error?" Preferiría evitar problemas no solo la mayor parte del tiempo, sino 100% del tiempo.
Hay una forma mejor: usar siempre la function
(para que siempre pueda vincularse dinámicamente), y hacer referencia siempre a través de una variable. Las variables son léxicas y asumen muchos nombres. Asignar this
a una variable dejará en claro tus intenciones:
function Reader() {
var reader = this;
reader.book.on(''change'', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
Además, siempre asignando this
a una variable (incluso cuando hay una sola o ninguna otra función) se garantiza que las intenciones de uno se mantengan claras incluso después de que se cambie el código.
Además, this
dinámica no es excepcional. jQuery se utiliza en más de 50 millones de sitios web (a partir de este escrito en febrero de 2016). Aquí hay otras API que enlazan dinámicamente:
- Mocha (~ 120k descargas ayer) expone métodos para sus pruebas a través de
this
. - Grunt (~ 63k descargas ayer) expone métodos para construir tareas a través de
this
. - Backbone (~ 22k descargas ayer) define métodos para acceder a
this
. - Las API de eventos (como las DOM) se refieren a un
EventTarget
conthis
. - Las API de prototipos que están parcheadas o extendidas se refieren a las instancias con
this
.
(Estadísticas a través de http://trends.builtwith.com/javascript/jQuery y https://www.npmjs.com .)
Es probable que necesites dinámicos this
enlaces ya.
Un léxico this
se espera a veces, pero a veces no; así como una dinámica this
veces se espera, pero a veces no. Afortunadamente, hay una mejor manera, que siempre produce y comunica el enlace esperado.
Con respecto a la sintaxis concisa
Las funciones de flecha lograron proporcionar una "forma sintáctica más corta" para las funciones. ¿Pero estas funciones más cortas te harán más exitoso?
Es x => x * x
"más fácil de leer" que la function (x) { return x * x; }
function (x) { return x * x; }
? Tal vez lo sea, porque es más probable que produzca una sola línea de código corta. Accoring to Dyson''s La influencia de la velocidad de lectura y la longitud de la línea en la efectividad de la lectura desde la pantalla ,
Una longitud de línea media (55 caracteres por línea) parece admitir una lectura efectiva a velocidades normales y rápidas. Esto produjo el más alto nivel de comprensión. . .
Se hacen justificaciones similares para el operador condicional (ternario) y para las instrucciones if
sola línea.
Sin embargo, ¿está realmente escribiendo las funciones matemáticas simples que se proposal ? Mis dominios no son matemáticos, por lo que mis subrutinas rara vez son tan elegantes. Más bien, normalmente veo que las funciones de flecha rompen el límite de una columna y se ajustan a otra línea debido al editor o la guía de estilo, que anula la "legibilidad" según la definición de Dyson.
Uno podría plantear, "¿Qué hay de usar la versión corta para funciones cortas, cuando sea posible?" Pero ahora una regla estilística contradice una restricción de lenguaje: "Intente utilizar la notación de función más corta posible, teniendo en cuenta que a veces solo la notación más larga se unirá a this
como se esperaba". Tal confusión hace que las flechas sean particularmente propensas al mal uso.
Hay numerosos problemas con la sintaxis de la función de flecha:
const a = x =>
doSomething(x);
const b = x =>
doSomething(x);
doSomethingElse(x);
Ambas funciones son sintácticamente válidas. Pero doSomethingElse(x);
no está en el cuerpo de b
, es solo una declaración de nivel superior, con sangría.
Cuando se expande a la forma de bloque, ya no hay un return
implícito, que podría olvidarse de restaurar. Pero es posible que la expresión solo haya tenido la intención de producir un efecto secundario, por lo tanto, ¿quién sabe si será necesario un return
explícito en el futuro?
const create = () => User.create();
const create = () => {
let user;
User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
const create = () => {
let user;
return User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
Lo que puede entenderse como un parámetro de reposo puede analizarse como el operador de propagación:
processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest
La asignación se puede confundir con los argumentos predeterminados:
const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens
Los bloques parecen objetos
(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it''s a labeled statement)
(id) => ({name: id}) // Returns an object
¿Qué significa esto?
() => {}
¿El autor intentó crear una no-op, o una función que devuelve un objeto vacío? (Teniendo esto en cuenta, ¿deberíamos colocar {
después =>
? ¿Deberíamos limitarnos a la sintaxis de expresión solamente? Eso reduciría aún más la frecuencia de las flechas).
=>
ve como <=
y >=
:
x => 1 ? 2 : 3
x <= 1 ? 2 : 3
if (x => 1) {}
if (x >= 1) {}
Para invocar una expresión de función de flecha inmediatamente, se debe colocar ()
en el exterior, pero colocar ()
en el interior es válido y podría ser intencional.
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
Aunque, si uno escribe (() => doSomething()());
con la intención de escribir una expresión de función invocada de inmediato, simplemente nada sucederá.
Es difícil argumentar que las funciones de flecha son "más comprensibles" teniendo en cuenta todos los casos anteriores. Uno podría aprender todas las reglas especiales requeridas para utilizar esta sintaxis. ¿Realmente vale la pena?
La sintaxis de la function
es excepcionalmente generalizada. Utilizar la function
exclusivamente significa que el propio lenguaje evita que uno escriba código confuso. Para escribir procedimientos que deben entenderse sintácticamente en todos los casos, elijo la function
.
Respecto a una pauta
Solicita una guía que debe ser "clara" y "coherente". El uso de las funciones de flecha eventualmente resultará en un código lógicamente inválido, sintácticamente válido, con ambas formas de funciones entrelazadas, significativa y arbitrariamente. Por lo tanto, ofrezco lo siguiente:
Guía para la notación de funciones en ES6:
- Siempre crea procedimientos con
function
. - Siempre asigna
this
a una variable. No utilice() => {}
.
Hace un tiempo, nuestro equipo migró todo su código (una aplicación AngularJS de tamaño mediano) a JavaScript compilado utilizando Traceur Babel . Ahora estoy usando la siguiente regla de oro para las funciones en ES6 y más allá:
- Utilice la
function
en el ámbito global y paraObject.prototype
propiedades deObject.prototype
. - Utilice la
class
para los constructores de objetos. - Use
=>
cualquier otro lugar.
¿Por qué usar las funciones de flecha en casi todas partes?
- Seguridad del alcance: cuando las funciones de flecha se usan de manera consistente, se garantiza que todo use el mismo
thisObject
como la raíz. Si incluso una única función estándar de devolución de llamada se mezcla con un montón de funciones de flecha, existe la posibilidad de que el alcance se desordene. - Compacidad: las funciones de flecha son más fáciles de leer y escribir. (Esto puede parecer opinado, así que daré algunos ejemplos más adelante).
- Claridad: cuando casi todo es una función de flecha, cualquier
function
regular sobresale inmediatamente para definir el alcance. Un desarrollador siempre puede buscar la siguiente declaración defunction
para ver qué es este objeto.
¿Por qué siempre usar funciones regulares en el alcance global o en el alcance del módulo?
- Para indicar una función que no debe acceder a este
thisObject
. - El objeto de la
window
(alcance global) se aborda mejor de manera explícita. - Muchas definiciones de
Object.prototype
se encuentran en el ámbito global (piense enString.prototype.truncate
etc.) y, por lo general, tienen que ser de tipofunction
. El uso constante de lafunction
en el ámbito global ayuda a evitar errores. - Muchas funciones en el ámbito global son constructores de objetos para definiciones de clase de estilo antiguo.
- Las funciones pueden ser nombradas 1 . Esto tiene dos ventajas: (1) Es menos incómodo escribir la
function foo(){}
queconst foo = () => {}
, en particular fuera de las llamadas a otras funciones. (2) El nombre de la función se muestra en los seguimientos de pila. Si bien sería tedioso nombrar cada devolución de llamada interna, nombrar todas las funciones públicas es probablemente una buena idea. - Las declaraciones de funciones se hoisted (lo que significa que se puede acceder a ellas antes de declararlas), que es un atributo útil en una función de utilidad estática.
Constructores de objetos
Intentar crear una instancia de una función de flecha produce una excepción:
var x = () => {};
new x(); // TypeError: x is not a constructor
Una ventaja clave de las funciones sobre las funciones de flecha es, por lo tanto, que funciona como constructores de objetos:
function Person(name) {
this.name = name;
}
Sin embargo, la definición de clase de borrador 2 Harmony funcionalmente idéntica es casi tan compacta:
class Person {
constructor(name) {
this.name = name;
}
}
Espero que el uso de la notación anterior eventualmente se desanime. La notación de constructor de objetos todavía puede ser utilizada por algunos para fábricas de objetos anónimos simples donde los objetos se generan mediante programación, pero no por mucho más.
Cuando se necesita un constructor de objetos, se debe considerar convertir la función a una class
como se muestra arriba. La sintaxis también funciona con funciones / clases anónimas.
La legibilidad de las funciones de flecha
El mejor argumento probablemente para adherirse a las funciones regulares (maldito sea la seguridad) es que las funciones de flecha son menos legibles que las funciones normales. Si su código no es funcional en primer lugar, entonces las funciones de flecha pueden no parecer necesarias, y cuando las funciones de flecha no se usan de manera consistente, se ven feas.
ECMAScript ha cambiado bastante desde que ECMAScript 5.1 nos dio el Array.forEach
funcional, Array.map
y todas estas funciones de programación funcional que nos Array.map
usar funciones donde los bucles for se hubieran usado antes. JavaScript asíncrono ha despegado bastante. ES6 también enviará un objeto Promise
, lo que significa aún más funciones anónimas. No hay vuelta atrás para la programación funcional. En JavaScript funcional, las funciones de flecha son preferibles a las funciones regulares.
Tomemos, por ejemplo, este pedazo de código 3 (particularmente confuso):
function CommentController(articles) {
this.comments = [];
articles.getList()
.then(articles => Promise.all(articles.map(article => article.comments.getList())))
.then(commentLists => commentLists.reduce((a, b) => a.concat(b)));
.then(comments => {
this.comments = comments;
})
}
La misma pieza de código con funciones regulares:
function CommentController(articles) {
this.comments = [];
articles.getList()
.then(function (articles) {
return Promise.all(articles.map(function (article) {
return article.comments.getList();
}));
})
.then(function (commentLists) {
return commentLists.reduce(function (a, b) {
return a.concat(b);
});
})
.then(function (comments) {
this.comments = comments;
}.bind(this));
}
Si bien cualquiera de las funciones de flecha puede ser reemplazada por una función estándar, habría muy poco que ganar al hacerlo. ¿Qué versión es más legible? Yo diría que el primero.
Creo que la cuestión de si usar las funciones de flecha o las funciones regulares se volverá menos relevante con el tiempo. La mayoría de las funciones se convertirán en métodos de clase, que eliminan la palabra clave de la function
, o se convertirán en clases. Las funciones permanecerán en uso para parchear las clases a través del Object.prototype
. Mientras tanto, sugiero reservar la palabra clave de function
para cualquier cosa que realmente debería ser un método de clase o una clase.
Notas
- Las funciones de flecha con nombre se han diferido en la especificación ES6 . Todavía podrían añadirse una versión futura.
- De acuerdo con el borrador de la especificación "Las declaraciones / expresiones de clase crean una función de constructor / par de prototipo exactamente como para las declaraciones de función" siempre que una clase no use la palabra clave
extend
. Una pequeña diferencia es que las declaraciones de clase son constantes, mientras que las declaraciones de funciones no lo son. - Nota sobre los bloques en las funciones de flecha de una sola declaración: Me gusta usar un bloque dondequiera que se llame a una función de flecha solo por efecto secundario (por ejemplo, asignación). De esa forma queda claro que el valor de retorno puede ser descartado.
Las funciones de flecha se crearon para simplificar el scope
función y resolver this
palabra clave haciéndola más simple. Utilizan la sintaxis =>
, que parece una flecha.
Nota: No reemplaza las funciones existentes. Si reemplaza cada sintaxis de funciones con funciones de flecha, no funcionará en todos los casos.
Veamos la sintaxis de ES5 existente. Si this
palabra clave estuviera dentro del método de un objeto (una función que pertenece a un objeto), ¿a qué se referiría?
var Actor = {
name: ''RajiniKanth'',
getName: function() {
console.log(this.name);
}
};
Actor.getName();
El fragmento anterior se referiría a un object
e imprimiría el nombre "RajiniKanth"
. Exploremos el siguiente fragmento y veamos lo que esto indicaría aquí.
var Actor = {
name: ''RajiniKanth'',
movies: [''Kabali'', ''Sivaji'', ''Baba''],
showMovies: function() {
this.movies.forEach(function(movie) {
alert(this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
Ahora, ¿qué pasaría si this
palabra clave estuviera dentro de method''s function
del method''s function
?
Aquí, esto se referiría a un window object
que a la inner function
ya que está fuera de scope
. Debido a que this
, siempre hace referencia al propietario de la función en la que se encuentra, en este caso, ya que ahora está fuera del alcance, la ventana / objeto global.
Cuando está dentro del método de un object
, el propietario de la function
es el objeto. Por lo tanto, esta palabra clave está vinculada al objeto. Sin embargo, cuando está dentro de una función, ya sea independiente o dentro de otro método, siempre se referirá a la window/global
objeto window/global
.
var fn = function(){
alert(this);
}
fn(); // [object Window]
Hay formas de resolver este problema en nuestro propio ES5
, veamos esto antes de sumergirnos en las funciones de flecha de ES6 sobre cómo resolverlo.
Normalmente, crearías una variable fuera de la función interna del método. Ahora el método ''forEach''
obtiene acceso a this
y, por lo tanto, a las propiedades object''s
y sus valores.
var Actor = {
name: ''RajiniKanth'',
movies: [''Kabali'', ''Sivaji'', ''Baba''],
showMovies: function() {
var _this = this;
this.movies.forEach(function(movie) {
alert(_this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
usando bind
para adjuntar la palabra clave this
que hace referencia al método a la method''s inner function
.
var Actor = {
name: ''RajiniKanth'',
movies: [''Kabali'', ''Sivaji'', ''Baba''],
showMovies: function() {
this.movies.forEach(function(movie) {
alert(_this.name + " has acted in " + movie);
}).bind(this);
}
};
Actor.showMovies();
Ahora, con la función de flecha ES6
, podemos tratar el problema del lexical scoping
de una manera más simple.
var Actor = {
name: ''RajiniKanth'',
movies: [''Kabali'', ''Sivaji'', ''Baba''],
showMovies: function() {
this.movies.forEach((movie) => {
alert(this.name + " has acted in " + movie);
});
}
};
Actor.showMovies();
Arrow functions
son más como sentencias de función, excepto que bind
esto al parent scope
. Si la arrow function is in top scope
, this
argumento se referirá a la window/global scope
, mientras que una función de flecha dentro de una función normal tendrá este argumento igual que su función externa.
Con arrow
funciones de arrow
, this
está vinculado al scope
de scope
en el momento de la creación y no se puede cambiar. El nuevo operador, enlazar, llamar y aplicar no tiene efecto en esto.
var asyncFunction = (param, callback) => {
window.setTimeout(() => {
callback(param);
}, 1);
};
// With a traditional function if we don''t control
// the context then can we lose control of `this`.
var o = {
doSomething: function () {
// Here we pass `o` into the async function,
// expecting it back as `param`
asyncFunction(o, function (param) {
// We made a mistake of thinking `this` is
// the instance of `o`.
console.log(''param === this?'', param === this);
});
}
};
o.doSomething(); // param === this? false
En el ejemplo anterior, perdimos el control de esto. Podemos resolver el ejemplo anterior usando una referencia variable de this
o usando bind
. Con ES6, se vuelve más fácil administrar this
ya que está vinculado al lexical scoping
.
var asyncFunction = (param, callback) => {
window.setTimeout(() => {
callback(param);
}, 1);
};
var o = {
doSomething: function () {
// Here we pass `o` into the async function,
// expecting it back as `param`.
//
// Because this arrow function is created within
// the scope of `doSomething` it is bound to this
// lexical scope.
asyncFunction(o, (param) => {
console.log(''param === this?'', param === this);
});
}
};
o.doSomething(); // param === this? true
Cuando no a las funciones de flecha
Dentro de un objeto literal.
var Actor = {
name: ''RajiniKanth'',
movies: [''Kabali'', ''Sivaji'', ''Baba''],
getName: () => {
alert(this.name);
}
};
Actor.getName();
Actor.getName
se define con una función de flecha, pero en la invocación alerta undefined porque this.name
undefined
está undefined
ya que el contexto permanece en la window
.
Ocurre porque la función de flecha enlaza el contexto de manera léxica con el window object
la window object
... es decir, el alcance externo. Ejecutar this.name
es equivalente a window.name
, que no está definido.
Prototipo de objeto
La misma regla se aplica al definir métodos en un prototype object
. En lugar de usar una función de flecha para definir el método sayCatName, que trae una context window
incorrecta:
function Actor(name) {
this.name = name;
}
Actor.prototype.getName = () => {
console.log(this === window); // => true
return this.name;
};
var act = new Actor(''RajiniKanth'');
act.getName(); // => undefined
Constructores invocadores
this
en una invocación de construcción es el objeto recién creado. Al ejecutar un nuevo Fn (), el contexto del constructor Fn
es un objeto nuevo: this instanceof Fn === true
.
this
se configura desde el contexto adjunto, es decir, el alcance externo que hace que no se asigne al objeto recién creado.
var Message = (text) => {
this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message(''Hello World!'');
Devolución de llamada con contexto dinámico
La función de flecha vincula el context
forma estática en la declaración y no es posible hacerlo dinámico. Adjuntar escuchas de eventos a elementos DOM es una tarea común en la programación del lado del cliente. Un evento activa la función de controlador con esto como el elemento de destino.
var button = document.getElementById(''myButton'');
button.addEventListener(''click'', () => {
console.log(this === window); // => true
this.innerHTML = ''Clicked button'';
});
this
es la ventana en una función de flecha que se define en el contexto global. Cuando ocurre un evento de clic, el navegador intenta invocar la función del controlador con el contexto del botón, pero la función de flecha no cambia su contexto predefinido. this.innerHTML
es equivalente a window.innerHTML
y no tiene sentido.
Tienes que aplicar una expresión de función, que permite cambiar esto dependiendo del elemento de destino:
var button = document.getElementById(''myButton'');
button.addEventListener(''click'', function() {
console.log(this === button); // => true
this.innerHTML = ''Clicked button'';
});
Cuando el usuario hace clic en el botón, esto en la función del controlador es el botón. Por this.innerHTML = ''Clicked button''
tanto, this.innerHTML = ''Clicked button''
modifica correctamente el texto del botón para reflejar el estado de clic.
Referencias: https://rainsoft.io/when-not-to-use-arrow-functions-in-javascript/
De una manera sencilla,
var a =20; function a(){this.a=10; console.log(a);}
//20, since the context here is window.
Otra instancia:
var a = 20;
function ex(){
this.a = 10;
function inner(){
console.log(this.a); //can you guess the output of this line.
}
inner();
}
var test = new ex();
Respuesta: La consola imprimiría 20.
La razón es que cada vez que se ejecuta una función, se crea su propia pila, en este ejemplo, la ex
función se ejecuta con el new
operador para que se cree un contexto, y cuando inner
se ejecuta, JS creará una nueva pila y ejecutará la inner
función global context
aunque existe una contexto local.
Entonces, si queremos que la inner
función tenga un contexto local, ex
entonces debemos vincular el contexto a la función interna.
Las flechas resuelven este problema, en lugar de tomar el Global context
que toman, local context
si existe alguno. En el given example,
tomará new ex()
como this
.
Por lo tanto, en todos los casos donde la vinculación es explícita, las flechas resuelven el problema por defecto.
Además de las excelentes respuestas hasta ahora, me gustaría presentar una razón muy diferente por la que las funciones de flecha son en cierto sentido fundamentalmente mejores que las funciones de JavaScript "normales". En aras de la discusión, asumamos temporalmente que usamos un comprobador de tipos como TypeScript o "Flow" de Facebook. Considere el siguiente módulo de juguete, que es un código ECMAScript 6 válido más anotaciones de tipo Flow: (Incluiré el código sin tipo, que sería el resultado realista de Babel, al final de esta respuesta, para que pueda ejecutarse).
export class C {
n : number;
f1: number => number;
f2: number => number;
constructor(){
this.n = 42;
this.f1 = (x:number) => x + this.n;
this.f2 = function (x:number) { return x + this.n;};
}
}
Ahora vea qué sucede cuando usamos la clase C de un módulo diferente, como este:
let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1: number = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2: number = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!
Como puede ver, el verificador de tipos falló aquí: se suponía que f2 devolvía un número, ¡pero devolvía una cadena!
Peor aún, parece que ningún comprobador de tipo concebible puede manejar funciones de JavaScript normales (sin flecha), porque "esto" de f2 no se produce en la lista de argumentos de f2, por lo que no se podría agregar el tipo requerido para "esto" como una anotación a f2.
¿Este problema también afecta a las personas que no usan las verificadoras de tipos? Creo que sí, porque incluso cuando no tenemos tipos estáticos, pensamos como si estuvieran allí. ("Los primeros parámetros deben ser un número, el segundo una cadena", etc.) Un "Este" documento oculto que puede o no ser usado en el cuerpo de la función hace que nuestra contabilidad mental sea más difícil.
Aquí está la versión ejecutable sin tipo, que sería producida por Babel:
class C {
constructor() {
this.n = 42;
this.f1 = x => x + this.n;
this.f2 = function (x) { return x + this.n; };
}
}
let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1 = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2 = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!
Prefiero usar las funciones de flecha en todo momento donde el acceso a local this
no sea necesario, porque la función de flecha no vincula sus propios argumentos, super, o new.target .