Доступ к закрытой (локальной) переменной внутри области закрытия - PullRequest
4 голосов
/ 12 мая 2011

Я делаю расширение Google Chrome и пытаюсь получить ссылку на локальную переменную в области закрытия.

// 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")

, поэтому я пытаюсь сделать в своем скрипте содержимого вставкуфункция в прототип Match только для получения переменных player_red и 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);
    }
}

, но это не будет работать, потому что player_red и player_blue не определены в this.

Я нашел этот вопрос через поиск.Решение состоит в том, чтобы "обернуть конструктор в новый конструктор и затем установить прототипы равными" .К сожалению, это не работает для меня, так как у меня нет доступа к оригинальному сценарию сайта и, возможно, потому что:

  • даже при создании нового myMatch, новый myMatch не работаетнаследовать переменные player_red и player_blue от их исходного экземпляра Match.
  • Есть ли возможные обходные пути?Спасибо.

1 Ответ

2 голосов
/ 12 мая 2011

Примечания к «частичному решению»:

Обратите внимание, что фрагменты кода, размещенные ниже, показывают только "некоторые альтернативы, которые могут или не могут предоставить достаточно, чтобы обойтись". Это потому, что они не захватывают значения (объекты Player) внутри конструктора, а только переносят значения, идущие внутрь.

«Полное решение» может также обернуть конструктор Player и использовать свойство или другой механизм, чтобы «запомнить» объекты, созданные для различных входных значений; альтернативно, он может запомнить порядок создания объекта . Затем его можно использовать для переноса Match, а затем извлекать созданных игроков из общего хранилища после запуска конструктора Match - однако эти детали оставляются в качестве упражнения. Код переноса Player может использовать код, представленный ниже (при условии, что Player является глобальным / доступным свойством).


Точный запрос невозможен с учетом вышеуказанного контекста.

Переменные (реальные переменные, а не свойства) доступны только из области, в которой они объявлены, или из вложенной области, поскольку они разрешаются через цепочки областей. Это также включает использование eval. Хотя это может показаться ограничением, оно также гарантирует, что цепочки областей видимости (и их переменные) не могут быть извлечены извне, если они не раскрыты.

Однако рассмотрим этот забавный подход, использующий тот факт, что явный объект можно return редактировать из Конструктора:

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
}

Конечно, это сломает такие вещи, как new Match(...) instanceof Match.

Удачного кодирования.


Обновление:

Вот модификация вышеописанного для работы с методом «обернуть конструктор в новый конструктор, а затем установить равные прототипы», как обсуждалось в ссылке в посте. Хитрость заключается в «краже» имени глобального свойства. Я также изменил код, чтобы oldMatch оставался закрытым, чтобы избежать загрязнения.

// 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)

В отличие от первого фрагмента кода, он должен работать с new Match(...) instanceof Match, но он все равно может сломаться в зависимости от конкретных предположений, сделанных в методах объекта Match.


Пример того, как инвертировать («извлечь») данные из конструктора 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
...