utiliza - ¿Por qué Javascript no une mi expresión de punto correctamente?
sintaxis de while (6)
Debido a que estos métodos se aplican en this
contexto, y en su ejemplo, this
no está definido
Una forma de anular esta variable utilizando el método bind
:
(true ? ''''.toLowerCase : ''''.toUpperCase).bind(''Hello'')();
esto devolverá hello
Esta pregunta ya tiene una respuesta aquí:
Me pregunto si los métodos de abstracción de puntos (por ejemplo, dog.bark
) se enlazan en tiempo de ejecución o en tiempo de compilación. Mi pregunta se refiere al siguiente código, que arroja un error:
(true ? ''''.toLowerCase : ''''.toUpperCase)()
Pero lo siguiente no:
true ? ''''.toLowerCase() : ''''.toUpperCase()
¿Por qué mi cadena literal no se resuelve en el primer ejemplo?
En el primer ejemplo, la expresión entre corchetes se evalúa primero, lo que da como resultado una referencia de función toLowerCase
o toLowerCase
y luego esta función se toLowerCase
sobre el resultado de la expresión ([expression])()
que no está definida. Esto es ilegal
Sin embargo, en el segundo ejemplo, toLowerCase
o toUpperCase
se toLowerCase
en la cadena vacía durante la evaluación de comparación terciaria y el valor de retorno de la función se asigna al operando del lado izquierdo. Asignar a indefinido no es ilegal.
''''[(true ? ''toLowerCase'' : ''toUpperCase'')]()
En el primer ejemplo, la función toLowerCase
se separa de su contexto (el objeto de cadena vacío) y luego se invoca. Como no vuelve a conectar la función a nada, no está undefined
como su contexto.
Este comportamiento existe para permitir la reutilización de código a través de mezclas:
var obj1 = {
name: "obj1",
getName: function() { return this.name; }
}
var obj2 = {
name: "obj2",
}
obj2.getName = obj1.getName //now obj2 has the getName method with correct context
console.log(obj2.getName())
Esto es realmente bastante simple una vez que obtienes cómo funcionan los métodos en JavaScript detrás de las escenas.
toUpperCase
es un método. Esta es una función que opera en un objeto específico ... generalmente a través de this
variable.
Javascript es un lenguaje prototípico ... lo que significa que las funciones asociadas a los objetos son solo funciones y se pueden copiar. Hay algo de trabajo detrás de la escena que asegura que esté configurado correctamente cuando llamas a un método, pero este trabajo solo ocurre cuando lo llamas como un método ... como en el obj.method()
.
En otras palabras: ''''.toUpperCase()
se asegura de que this
esté configurado en la cadena ''''
cuando lo llame.
Cuando lo llamas como toUpperCase()
this
no está configurado para nada en particular (diferentes ambientes hacen cosas diferentes con this
en este caso)
Lo que hace su código podría reescribirse así:
var function_to_call;
if (true) {
function_to_call = ''''.toLowerCase;
} else {
function_to_call = ''''.toUpperCase;
}
function_to_call();
Debido a que su llamada de función: function_to_call()
no está en la sintaxis object.method()
, lo que establece this
para el objeto correcto no está hecho, y su llamada de función se ejecuta con this
no establecido en lo que desea.
Como han señalado otras personas, puede usar func.call(thing_to_make_this)
o func.apply()
para adjuntar lo correcto a esto explícitamente.
Me resulta mucho más útil utilizar .bind()
- que es extremadamente .bind()
en mi opinión. function_name.bind(this_object)
te da una nueva función que siempre tendrá this
adjunto a la cosa correcta:
// assuming function_to_call is set
function_that_works = function_to_call.bind(my_object)
function_that_works(); // equivalent to my_object.function_to_call()
y esto significa que puede pasar la función que obtiene de bind()
como lo haría con una función normal, y funcionará en el objeto que desee. Esto es especialmente útil en las devoluciones de llamada, ya que puede crear una función anónima que esté vinculada al objeto en el que se creó:
// this won''t work because when this runs, ''this'' doesn''t mean what you think
setTimeout(function() { this.display_message(''success''); }, 2000);
// this will work, because we have given setTimeout a pre-bound function.
setTimeout(function() { this.display_message(''success''); }.bind(this), 2000);
TL; DR: No puede llamar a un método como una función y esperar que funcione, porque no sabe lo que debería ser. Si desea usar esa función, debe usar .call()
, .apply()
o .bind()
para asegurarse de que esté configurado correctamente para cuando se ejecuta la función.
Espero que ayude.
Porque cuando lo haces (true ? ''''.toLowerCase : ''''.toUpperCase)()
no estás llamando a la función que está vinculada a una cadena. Simplemente está llamando a la función sin ningún contexto.
Considere el siguiente ejemplo:
var obj = {
objname: "objname",
getName: function() {
return this.objname;
}
}
Cuando lo llamas con obj.getName()
, devuelve correctamente el valor, pero cuando haces algo como esto:
var fn = obj.getName
fn() // returns undefined because `fn` is not bound to `obj`
(true ? ''''.toLowerCase : ''''.toUpperCase)()
es equivalente a:
String.prototype.toLowerCase.call()
String.prototype.toLowerCase.call(undefined)
Sin embargo,
true ? ''''.toLowerCase() : ''''.toUpperCase()
es equivalente a:
String.prototype.toLowerCase.call('''')
En ambos casos, el primer argumento para call
se convierte en un objeto, al cual hará referencia this
en String.prototype.toLowerCase
.
undefined
no se puede convertir en un objeto, pero la cadena vacía puede:
function logThis () { console.log(this); }
logThis.call('''');
La consola del snippet SO solo muestra {}
, pero en realidad es lo mismo que obtienes del new String('''')
. Lea sobre el envoltorio de cadenas en MDN .