La this
palabra clave se comporta de manera diferente en JavaScript en comparación con otros idiomas. En lenguajes orientados a objetos, la this
palabra clave se refiere a la instancia actual de la clase. En JavaScript, el valor de this
está determinado por el contexto de invocación de function ( context.function()
) y dónde se llama.
1. Cuando se usa en contexto global
Cuando se usa this
en contexto global, está vinculado al objeto global ( window
en el navegador)
document.write(this); //[object Window]
Cuando se usa this
dentro de una función definida en el contexto global, this
todavía está vinculada al objeto global, ya que la función se convierte en un método de contexto global.
function f1()
{
return this;
}
document.write(f1()); //[object Window]
Arriba f1
se hace un método de objeto global. Por lo tanto, también podemos llamarlo en el window
objeto de la siguiente manera:
function f()
{
return this;
}
document.write(window.f()); //[object Window]
2. Cuando se usa dentro del método de objeto
Cuando utiliza una this
palabra clave dentro de un método de objeto, this
está vinculado al objeto que encierra "inmediato".
var obj = {
name: "obj",
f: function () {
return this + ":" + this.name;
}
};
document.write(obj.f()); //[object Object]:obj
Arriba he puesto la palabra inmediata entre comillas dobles. Es para señalar que si anida el objeto dentro de otro objeto, entonces this
está vinculado al padre inmediato.
var obj = {
name: "obj1",
nestedobj: {
name:"nestedobj",
f: function () {
return this + ":" + this.name;
}
}
}
document.write(obj.nestedobj.f()); //[object Object]:nestedobj
Incluso si agrega la función explícitamente al objeto como método, sigue las reglas anteriores, es decir, this
apunta al objeto primario inmediato.
var obj1 = {
name: "obj1",
}
function returnName() {
return this + ":" + this.name;
}
obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1
3. Al invocar la función sin contexto
Cuando utiliza this
una función interna que se invoca sin ningún contexto (es decir, no en ningún objeto), está vinculada al objeto global ( window
en el navegador) (incluso si la función está definida dentro del objeto).
var context = "global";
var obj = {
context: "object",
method: function () {
function f() {
var context = "function";
return this + ":" +this.context;
};
return f(); //invoked without context
}
};
document.write(obj.method()); //[object Window]:global
Probándolo todo con funciones
También podemos probar los puntos anteriores con funciones. Sin embargo, hay algunas diferencias.
- Arriba agregamos miembros a los objetos usando notación literal de objetos. Podemos agregar miembros a las funciones mediante el uso
this
. para especificarlos.
- La notación literal de objeto crea una instancia de objeto que podemos usar de inmediato. Con la función, es posible que primero necesitemos crear su instancia utilizando el
new
operador.
- También en un enfoque literal de objeto, podemos agregar explícitamente miembros a un objeto ya definido utilizando el operador de punto. Esto se agrega solo a la instancia específica. Sin embargo, he agregado una variable al prototipo de la función para que se refleje en todas las instancias de la función.
A continuación probé todas las cosas que hicimos con Object y this
superiores, pero primero creé la función en lugar de escribir directamente un objeto.
/*********************************************************************
1. When you add variable to the function using this keyword, it
gets added to the function prototype, thus allowing all function
instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
this.name = "ObjDefinition";
this.getName = function(){
return this+":"+this.name;
}
}
obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition
/*********************************************************************
2. Members explicitly added to the function protorype also behave
as above: all function instances have their own copy of the
variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
return "v"+this.version; //see how this.version refers to the
//version variable added through
//prototype
}
document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************
3. Illustrating that the function variables added by both above
ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1
obj2.incrementVersion(); //incrementing version in obj2
//does not affect obj1 version
document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************
4. `this` keyword refers to the immediate parent object. If you
nest the object through function prototype, then `this` inside
object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj',
getName1 : function(){
return this+":"+this.name;
}
};
document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj
/*********************************************************************
5. If the method is on an object's prototype chain, `this` refers
to the object the method was called on, as if the method was on
the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
//as its prototype
obj3.a = 999; //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
//calling obj3.fun() makes
//ProtoObj.fun() to access obj3.a as
//if fun() is defined on obj3
4. Cuando se usa dentro de la función constructora .
Cuando la función se usa como un constructor (es decir, cuando se llama con una new
palabra clave), el this
cuerpo interno de la función apunta al nuevo objeto que se está construyendo.
var myname = "global context";
function SimpleFun()
{
this.myname = "simple function";
}
var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
// object being constructed thus adding any member
// created inside SimipleFun() using this.membername to the
// object being constructed
//2. And by default `new` makes function to return newly
// constructed object if no explicit return value is specified
document.write(obj1.myname); //simple function
5. Cuando se usa dentro de la función definida en la cadena del prototipo
Si el método está en la cadena de prototipo de un objeto, this
dentro de dicho método se hace referencia al objeto en el que se llamó al método, como si el método estuviera definido en el objeto.
var ProtoObj = {
fun: function () {
return this.a;
}
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun()
//to be the method on its prototype chain
var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999
//Notice that fun() is defined on obj3's prototype but
//`this.a` inside fun() retrieves obj3.a
6. Dentro de las funciones call (), apply () y bind ()
- Todos estos métodos están definidos en
Function.prototype
.
- Estos métodos permiten escribir una función una vez e invocarla en un contexto diferente. En otras palabras, permiten especificar el valor
this
que se utilizará mientras se ejecuta la función. También toman cualquier parámetro para pasar a la función original cuando se invoca.
fun.apply(obj1 [, argsArray])
Se establece obj1
como el valor de this
inside fun()
y llama a los fun()
elementos que pasan argsArray
como sus argumentos.
fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- Establece obj1
como el valor de this
inside fun()
y llama a fun()
pasar arg1, arg2, arg3, ...
como sus argumentos.
fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- Devuelve la referencia a la función fun
con this
diversión interna vinculada a obj1
y parámetros fun
vinculados a los parámetros especificados arg1, arg2, arg3,...
.
- Por ahora la diferencia entre
apply
, call
y bind
debe haberse hecho evidente. apply
permite especificar los argumentos para funcionar como un objeto tipo matriz, es decir, un objeto con una length
propiedad numérica y propiedades enteras no negativas correspondientes. Mientras que call
permite especificar los argumentos de la función directamente. Ambos apply
e call
inmediatamente invocan la función en el contexto especificado y con los argumentos especificados. Por otro lado, bind
simplemente devuelve la función vinculada al this
valor especificado y los argumentos. Podemos capturar la referencia a esta función devuelta asignándola a una variable y luego podemos llamarla en cualquier momento.
function add(inc1, inc2)
{
return this.a + inc1 + inc2;
}
var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
//above add.call(o,5,6) sets `this` inside
//add() to `o` and calls add() resulting:
// this.a + inc1 + inc2 =
// `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
// `o.a` i.e. 4 + 5 + 6 = 15
var g = add.bind(o, 5, 6); //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />"); //15
var h = add.bind(o, 5); //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
// 4 + 5 + 6 = 15
document.write(h() + "<br />"); //NaN
//no parameter is passed to h()
//thus inc2 inside add() is `undefined`
//4 + 5 + undefined = NaN</code>
7. this
controladores de eventos internos
- Cuando asigna una función directamente a los manejadores de eventos de un elemento, el uso de la
this
función de manejo de eventos directamente dentro se refiere al elemento correspondiente. Dicha asignación directa de funciones se puede hacer utilizando el addeventListener
método o mediante los métodos tradicionales de registro de eventos como onclick
.
- Del mismo modo, cuando se usa
this
directamente dentro de la propiedad de evento (like <button onclick="...this..." >
) del elemento, se refiere al elemento.
- Sin embargo, el uso
this
indirecto a través de la otra función llamada dentro de la función de manejo de eventos o propiedad de evento se resuelve en el objeto global window
.
- El mismo comportamiento anterior se logra cuando adjuntamos la función al controlador de eventos utilizando el método del modelo de Registro de eventos de Microsoft
attachEvent
. En lugar de asignar la función al controlador de eventos (y al hacer así el método de función del elemento), llama a la función en el evento (efectivamente lo llama en contexto global).
Recomiendo probar esto mejor en JSFiddle .
<script>
function clickedMe() {
alert(this + " : " + this.tagName + " : " + this.id);
}
document.getElementById("button1").addEventListener("click", clickedMe, false);
document.getElementById("button2").onclick = clickedMe;
document.getElementById("button5").attachEvent('onclick', clickedMe);
</script>
<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>
<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />
<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />
IE only: <button id="button5">click() "attached" using attachEvent() </button>
8. this
en la función de flecha ES6
En una función de flecha, this
se comportará como variables comunes: se heredará de su ámbito léxico. Las funciones this
, donde se define la función de flecha, serán las funciones de flecha this
.
Entonces, ese es el mismo comportamiento que:
(function(){}).bind(this)
Ver el siguiente código:
const globalArrowFunction = () => {
return this;
};
console.log(globalArrowFunction()); //window
const contextObject = {
method1: () => {return this},
method2: function(){
return () => {return this};
}
};
console.log(contextObject.method1()); //window
const contextLessFunction = contextObject.method1;
console.log(contextLessFunction()); //window
console.log(contextObject.method2()()) //contextObject
const innerArrowFunction = contextObject.method2();
console.log(innerArrowFunction()); //contextObject