validar validacion vacios formularios formulario enviar ejemplos con campos attribute antes javascript callback this

validacion - validar formulario javascript html5



¿Cómo acceder al ''este'' correcto dentro de una devolución de llamada? (8)

Tengo una función constructora que registra un controlador de eventos:

function MyConstructor(data, transport) { this.data = data; transport.on(''data'', function () { alert(this.data); }); } // Mock transport object var transport = { on: function(event, callback) { setTimeout(callback, 1000); } }; // called as var obj = new MyConstructor(''foo'', transport);

Sin embargo, no puedo acceder a la propiedad de data del objeto creado dentro de la devolución de llamada. Parece que this no se refiere al objeto que fue creado sino a otro.

También intenté usar un método de objeto en lugar de una función anónima:

function MyConstructor(data, transport) { this.data = data; transport.on(''data'', this.alert); } MyConstructor.prototype.alert = function() { alert(this.name); };

Pero exhibe los mismos problemas.

¿Cómo puedo acceder al objeto correcto?


Aquí hay varias formas de acceder al contexto principal dentro del contexto secundario:

  1. Puede utilizar la función bind () .
  2. Almacene la referencia al contexto / esto dentro de otra variable (vea el ejemplo a continuación).
  3. Usa las funciones de Arrow ES6.
  4. Altere el diseño de código / función / arquitectura - para esto debe tener comando sobre los patrones de diseño en javascript.

1. Utilice la función de bind()

function MyConstructor(data, transport) { this.data = data; transport.on(''data'', ( function () { alert(this.data); }).bind(this) ); } // Mock transport object var transport = { on: function(event, callback) { setTimeout(callback, 1000); } }; // called as var obj = new MyConstructor(''foo'', transport);

Si está utilizando underscore.js - http://underscorejs.org/#bind

transport.on(''data'', _.bind(function () { alert(this.data); }, this));

2 Almacenar referencia a contexto / esto dentro de otra variable

function MyConstructor(data, transport) { var self = this; this.data = data; transport.on(''data'', function() { alert(self.data); }); }

3 funciones de flecha

function MyConstructor(data, transport) { this.data = data; transport.on(''data'', () => { alert(this.data); }); }


Lo que debes saber sobre this

this (también conocida como "el contexto") es una palabra clave especial dentro de cada función y su valor solo depende de cómo se llamó a la función, no cómo / cuándo / dónde se definió. No se ve afectado por los ámbitos léxicos como otras variables (a excepción de las funciones de flecha, ver más abajo). Aquí hay unos ejemplos:

function foo() { console.log(this); } // normal function call foo(); // `this` will refer to `window` // as object method var obj = {bar: foo}; obj.bar(); // `this` will refer to `obj` // as constructor function new foo(); // `this` will refer to an object that inherits from `foo.prototype`

Para obtener más información sobre this , eche un vistazo a la documentación de MDN .

¿Cómo referirse al correcto this

No uses this

En realidad, no desea acceder a this en particular, sino al objeto al que hace referencia . Es por eso que una solución fácil es simplemente crear una nueva variable que también se refiera a ese objeto. La variable puede tener cualquier nombre, pero los comunes son self y that .

function MyConstructor(data, transport) { this.data = data; var self = this; transport.on(''data'', function() { alert(self.data); }); }

Dado que self es una variable normal, obedece a las reglas de alcance léxico y es accesible dentro de la devolución de llamada. Esto también tiene la ventaja de que puede acceder a this valor de la devolución de llamada.

Establece explícitamente this de la devolución de llamada - parte 1

Puede parecer que no tiene control sobre el valor de this porque su valor se establece automáticamente, pero en realidad no es así.

Cada función tiene el método .bind [docs] , que devuelve una nueva función con this límite a un valor. La función tiene exactamente el mismo comportamiento que el que .bind , solo que this lo configuraste tú. No importa cómo o cuándo se llame a esa función, this siempre se referirá al valor pasado.

function MyConstructor(data, transport) { this.data = data; var boundFunction = (function() { // parenthesis are not necessary alert(this.data); // but might improve readability }).bind(this); // <- here we are calling `.bind()` transport.on(''data'', boundFunction); }

En este caso, estamos vinculando la devolución de llamada de this al valor de MyConstructor ''s this .

Nota: cuando vincule el contexto para jQuery, use jQuery.proxy [docs] en su lugar. La razón para hacer esto es para que no necesite almacenar la referencia a la función al desvincular una devolución de llamada de evento. jQuery maneja eso internamente.

ECMAScript 6: Usa funciones de flecha

ECMAScript 6 introduce funciones de flecha , que pueden considerarse funciones lambda. Ellos no tienen su propia unión. En su lugar, se busca en el alcance como una variable normal. Eso significa que no tienes que llamar .bind . Ese no es el único comportamiento especial que tienen, consulte la documentación de MDN para obtener más información.

function MyConstructor(data, transport) { this.data = data; transport.on(''data'', () => alert(this.data)); }

Ajuste this de la devolución de llamada - parte 2

Algunas funciones / métodos que aceptan devoluciones de llamada también aceptan un valor al que debería referirse la devolución de llamada. Básicamente, esto es lo mismo que enlazarlo usted mismo, pero la función / método lo hace por usted. Array#map [docs] es un método de este tipo. Su firma es:

array.map(callback[, thisArg])

El primer argumento es la devolución de llamada y el segundo argumento es el valor al que debe referirse. Aquí hay un ejemplo artificial:

var arr = [1, 2, 3]; var obj = {multiplier: 42}; var new_arr = arr.map(function(v) { return v * this.multiplier; }, obj); // <- here we are passing `obj` as second argument

Nota: si se puede o no pasar un valor para this generalmente se menciona en la documentación de esa función / método. Por ejemplo, el método $.ajax jQuery [docs] describe una opción llamada context :

Este objeto se convertirá en el contexto de todas las devoluciones de llamadas relacionadas con Ajax.

Problema común: uso de métodos de objetos como devoluciones de llamada / controladores de eventos

Otra manifestación común de este problema es cuando un método de objeto se utiliza como un controlador de devolución de llamada / evento. Las funciones son ciudadanos de primera clase en JavaScript y el término "método" es solo un término coloquial para una función que es un valor de una propiedad de objeto. Pero esa función no tiene un enlace específico a su objeto "que contiene".

Considere el siguiente ejemplo:

function Foo() { this.data = 42, document.body.onclick = this.method; } Foo.prototype.method = function() { console.log(this.data); };

La función this.method se asigna como un controlador de eventos de clic, pero si se hace clic en document.body , el valor registrado no estará undefined , ya que dentro del controlador de eventos, this refiere al document.body , no a la instancia de Foo .
Como ya se mencionó al principio, a lo que se refiere this depende de cómo se llama la función, no de cómo se define .
Si el código era como el siguiente, podría ser más obvio que la función no tiene una referencia implícita al objeto:

function method() { console.log(this.data); } function Foo() { this.data = 42, document.body.onclick = this.method; } Foo.prototype.method = method;

La solución es la misma que se mencionó anteriormente: si está disponible, use .bind para vincularlo explícitamente a un valor específico

document.body.onclick = this.method.bind(this);

o llame explícitamente a la función como un "método" del objeto, utilizando una función anónima como controlador de devolución de llamadas / eventos y asigne el objeto ( this ) a otra variable:

var self = this; document.body.onclick = function() { self.method(); };

o use una función de flecha:

document.body.onclick = () => this.method();


El problema con el "contexto"

El término "contexto" se usa a veces para referirse al objeto referenciado por esto . Su uso no es apropiado porque no se ajusta ni semánticamente ni técnicamente a esto de ECMAScript .

"Context" significa las circunstancias que rodean algo que agrega significado, o alguna información anterior y siguiente que le da un significado adicional. El término "contexto" se usa en ECMAScript para referirse al contexto de ejecución , que es todos los parámetros, alcance y esto dentro del alcance de algún código en ejecución.

Esto se muestra en la sección 10.4.2 de ECMA-262 :

Establezca ThisBinding en el mismo valor que ThisBinding del contexto de ejecución de llamada

lo que indica claramente que esto es parte de un contexto de ejecución.

Un contexto de ejecución proporciona la información circundante que agrega significado al código que se está ejecutando. Incluye mucha más información que esta thisBinding .

Entonces el valor de esto no es "contexto", es solo una parte de un contexto de ejecución. Es esencialmente una variable local que puede establecerse mediante la llamada a cualquier objeto y en modo estricto, a cualquier valor.


Actualmente hay otro enfoque posible si las clases se usan en código.

Con el apoyo de los campos de clase es posible hacerlo de la siguiente manera:

class someView { onSomeInputKeyUp = (event) => { console.log(this); // this refers to correct value // .... someInitMethod() { //... someInput.addEventListener(''input'', this.onSomeInputKeyUp)

Seguramente, bajo el capó, toda la función de flecha buena anterior es la que vincula el contexto, pero en esta forma parece mucho más claro que la vinculación explícita.

Como se trata de la Propuesta de la Etapa 3, necesitará babel y el complemento babel adecuado para procesarlo como ahora (08/2018).


No podemos vincular esto a setTimeout() , ya que siempre se ejecuta con un objeto global (Ventana) , si desea acceder a this contexto en la función de devolución de llamada, mediante el uso de bind() a la función de devolución de llamada podemos lograr como:

setTimeout(function(){ this.methodName(); }.bind(this), 2000);


Otro enfoque, que es la forma estándar desde DOM2 para enlazar this dentro del detector de eventos, que le permite eliminar siempre el escucha (entre otras ventajas), es el handleEvent(evt) de la interfaz EventListener :

var obj = { handleEvent(e) { // always true console.log(this === obj); } }; document.body.addEventListener(''click'', obj);

Puede encontrar información detallada sobre el uso de handleEvent aquí: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38


Primero, debe tener una comprensión clara del scope y el comportamiento de this palabra clave en el contexto del scope .

this y scope :

there are two types of scope in javascript. They are : 1) Global Scope 2) Function Scope

en resumen, el alcance global se refiere al objeto de la ventana. Las variables declaradas en un alcance global son accesibles desde cualquier lugar. Por otra parte, el alcance de la función reside dentro de una función. La variable declarada dentro de una función no puede accederse desde el mundo exterior normalmente. this palabra clave en el ámbito global se refiere al objeto de la ventana. this función interna también se refiere al objeto de la ventana. Por this , siempre se referirá a la ventana hasta que encontremos una manera de manipular this para indicar un contexto de nuestra elección.

-------------------------------------------------------------------------------- - - - Global Scope - - ( globally "this" refers to window object) - - - - function outer_function(callback){ - - - - // outer function scope - - // inside outer function"this" keyword refers to window object - - - callback() // "this" inside callback also refers window object - - } - - - - function callback_function(){ - - - - // function to be passed as callback - - - - // here "THIS" refers to window object also - - - - } - - - - outer_function(callback_function) - - // invoke with callback - --------------------------------------------------------------------------------

Diferentes formas de manipular this funciones internas de devolución de llamada:

Aquí tengo una función constructora llamada Persona. Tiene una propiedad llamada name y cuatro métodos llamados sayNameVersion1 , sayNameVersion2 , sayNameVersion3 , sayNameVersion4 . Los cuatro tienen una tarea específica. Aceptar una devolución de llamada e invocarla. La devolución de llamada tiene una tarea específica que consiste en registrar la propiedad de nombre de una instancia de la función de constructor de persona.

function Person(name){ this.name = name this.sayNameVersion1 = function(callback){ callback.bind(this)() } this.sayNameVersion2 = function(callback){ callback() } this.sayNameVersion3 = function(callback){ callback.call(this) } this.sayNameVersion4 = function(callback){ callback.apply(this) } } function niceCallback(){ // function to be used as callback var parentObject = this console.log(parentObject) }

Ahora vamos a crear una instancia desde el constructor de persona e invocar diferentes versiones del sayNameVersionX (X se refiere a 1,2,3,4) con niceCallback para ver de cuántas maneras podemos manipular this devolución de llamada interna para referirnos a la instancia de person .

var p1 = new Person(''zami'') // create an instance of Person constructor

enlazar

Lo que se debe hacer es crear una nueva función con this palabra clave establecida en el valor proporcionado.

sayNameVersion1 y sayNameVersion2 usan bind para manipular this de la función de devolución de llamada.

this.sayNameVersion1 = function(callback){ callback.bind(this)() } this.sayNameVersion2 = function(callback){ callback() }

el primero enlaza this con la devolución de llamada dentro del propio método. Y para el segundo, la devolución de llamada se pasa con el objeto vinculado a él.

p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback

llamada :

El first argument del método de call se usa como this dentro de la función que se invoca con la call adjunta.

sayNameVersion3 usa call para manipular this para referirse al objeto person que creamos, en lugar del objeto window.

this.sayNameVersion3 = function(callback){ callback.call(this) }

y se llama así:

p1.sayNameVersion3(niceCallback)

aplicar :

De forma similar a la call , el primer argumento de apply refiere al objeto que se indicará con this palabra clave.

sayNameVersion4 utiliza se apply para manipular this para referirse al objeto person

this.sayNameVersion4 = function(callback){ callback.apply(this) }

y se llama como la siguiente. Simplemente se pasa la devolución de llamada,

p1.sayNameVersion4(niceCallback)


Todo está en la sintaxis "mágica" de llamar a un método:

object.property();

Cuando obtiene la propiedad del objeto y la llama de una sola vez, el objeto será el contexto del método. Si llama al mismo método, pero en pasos separados, el contexto es el ámbito global (ventana) en su lugar:

var f = object.property; f();

Cuando obtiene la referencia de un método, ya no se adjunta al objeto, solo es una referencia a una función simple. Lo mismo sucede cuando obtiene la referencia para usar como devolución de llamada:

this.saveNextLevelData(this.setAll);

Ahí es donde enlazarías el contexto a la función:

this.saveNextLevelData(this.setAll.bind(this));

Si está utilizando jQuery, debería usar el método $.proxy lugar, ya que el bind no es compatible con todos los navegadores:

this.saveNextLevelData($.proxy(this.setAll, this));