Acceda a una variable privada (local) dentro de un alcance de cierre
-
12-11-2019 - |
Pregunta
Estoy creando una extensión de Google Chrome e intentando obtener una referencia de una variable local dentro de un alcance de cierre.
// The script model of the target website
// I can't change any code of these
function Player(playerName){
this.name = playerName;
this.score = 0;
}
function Match(playerRed,playerBlue){
var player_red = new Player(playerRed);
var player_blue = new Player(playerBlue);
}
var tennis = new Match("Mike","John")
Entonces, lo que estoy tratando de hacer en mi script de contenido es inyectar una función en el prototipo de Match
solo para obtener la variable player_red
y player_blue
:
function Match(playerRed,playerBlue){
var player_red = new Player(playerRed);
var player_blue = new Player(playerBlue);
//hoping to add this into Match.prototype
this.showMatchInfo = function(){
alert(player_red.name + " vs " + player_blue.name);
}
}
pero esto no funcionará porque player_red
y player_blue
no está definido en this
.
encontré esto pregunta a través de la búsqueda.La solución es "envuelva el constructor en un nuevo constructor y luego iguale los prototipos".Lamentablemente, esto no funciona para mí porque no tengo acceso al script original del sitio web y probablemente porque:
- incluso creando nuevos
myMatch
, el nuevomyMatch
no no hereda elplayer_red
yplayer_blue
variable de su originalMatch
instancia. - ¿Existen posibles soluciones?Gracias.
Solución
Notas sobre la "solución parcial":
Tenga en cuenta que los fragmentos de código publicados a continuación solo muestran "algunas alternativas que pueden proporcionar o no lo suficiente para salir adelante".Esto se debe a que no capturan los valores (objetos Player) dentro del constructor, sino que solo envuelven los valores que van dentro.
Una "solución completa" también podría incluir el constructor Player y utilizar una propiedad u otro mecanismo para "recordar" los objetos creados para diferentes valores de entrada;alternativamente, podría recordar el orden de creación de los objetos..Esto luego podría usarse para envolver Match y luego extracto los jugadores creados desde la tienda compartida después de que se haya ejecutado el constructor del partido; sin embargo, esos detalles se dejan como ejercicio.El código de ajuste del Reproductor puede utilizar el código que se presenta a continuación (asumiendo que el Reproductor es una propiedad global/accesible).
La solicitud exacta no es posible dado el contexto anterior.
Solo se puede acceder a las variables (variables reales, no propiedades) desde el alcance en el que están declaradas o desde un alcance anidado, ya que se resuelven a través de cadenas de alcance.Esto también incluye el uso de eval
.Si bien esto puede parecer una limitación, también garantiza que las cadenas de alcance (y sus variables) no puedan ser manipuladas externamente a menos que se expongan.
Sin embargo, considere este enfoque divertido, que utiliza el hecho de que un objeto explícito puede ser return
ed de un constructor:
var oldMatch = Match
// note this form, else above would be pre-clobbered
Match = function Match (playerRed, playerBlue) {
var m = new oldMatch(playerRed, playerBlue)
// either "inject" method here, or save in object for later
m.myPlayerRed = playerRed
m.myPlayerBlue = playerBlue
return m
}
Por supuesto, esto romperá cosas como new Match(...) instanceof Match
.
Feliz codificación.
Actualizar:
Aquí hay una modificación de lo anterior para trabajar con el método "envolver el constructor en un nuevo constructor y luego establecer los prototipos iguales" como se explica en el enlace de la publicación.El truco consiste en "robar" el nombre de las propiedades globales.También he modificado el código para mantener oldMatch
"privado" para evitar la contaminación.
// note this form, else Match property would be pre-clobbered
Match = (function (oldMatch) {
function Match (playerRed, playerBlue) {
oldMatch.call(this, playerRed, playerBlue);
// either "inject" method here, or save in object for later
this.myPlayerRed = playerRed
this.myPlayerBlue = playerBlue
}
Match.prototype = oldMatch.prototype
return Match
})(Match)
A diferencia del primer fragmento de código, esto debería funcionar con new Match(...) instanceof Match
, pero aún así puede fallar dependiendo de suposiciones particulares hechas dentro de los métodos del objeto Match.
Ejemplo de cómo invertir ("extraer") datos del constructor Player:
// original -- remember this method will only work
// if Player is used as a property (and not itself a closure'd variable)
function Player (name) {
this.name = name
}
Player = (function (oldPlayer) {
function Player (name) {
oldPlayer.call(this, name)
var fn = arguments.callee
fn.recent = fn.recent || []
fn.recent.push([name, this])
}
Player.prototype = oldPlayer.prototype
return Player
})(Player)
var p1 = new Player("fred");
var p2 = new Player("barney");
alert("instanceof check? " + p1 instanceof Player)
alert("name check? " + ("barney" == p2.name))
alert(Player.recent.join(","))
Player.recent = [] // reset