Javascript - получить переменную изнутри локальной области видимости функции - PullRequest
0 голосов
/ 29 октября 2009

У меня не все в порядке, кроме базового JavaScript, поэтому, пожалуйста, прости простой вопрос.

Я использую библиотеку jsDraw2D . Эта библиотека имеет графический объект, который выглядит примерно так:

function jsGraphics(canvasDivElement) {
  var canvasDiv;
  this.drawLine = drawLine;
  function drawLine(point1, point2) {
    // do things with canvasDiv
  }
}

Вы используете это так:

var gr = new jsGraphics(document.getElementById('canvas'))
gr.drawLine(new jsPoint(0,0), new jsPoint(10,10))

Я хотел бы добавить функцию в jsGraphics, чтобы я мог вызвать

gr.getCanvasElement()

Есть ли способ сделать это без редактирования самой библиотеки?

Я пытался

jsGraphics.prototype.getCanvasElement = function() { return canvasDiv }

но это не похоже на работу. У меня есть интуитивное чувство, что это что-то с этим новым ключевым словом, но если бы вы могли объяснить, почему это не так, это тоже было бы полезно.

Ответы [ 3 ]

4 голосов
/ 29 октября 2009

Нет, здесь не используется обычное наследование на основе прототипов JavaScript, оно добавляет отдельную функцию drawLine к каждому экземпляру jsGraphics, каждый с закрытием вокруг собственной переменной canvasDiv.

Как только function jsGraphics() { закрывается }, больше нет никакого доступа к переменной canvasDiv вообще, если только одна из функций внутри не обеспечивает доступ к ней. Это часто делается намеренно для создания приватных переменных, явно, чтобы не дать вам попасть в canvasDiv.

3 голосов
/ 29 октября 2009

Вы не можете просто обязательно попасть к элементу canvasDiv, потому что, если он никогда не назначается объекту в конструкторе с помощью ключевого слова this, ссылка на этот объект существует в замыкании, созданном самой функцией конструктора.

Однако вы можете обернуть конструктор в новый конструктор и затем установить прототипы равными:

function myJsGraphics(canvasDivElement) {
  this.canvasDiv = canvasDivElement;
  jsGraphics.call(this, cavasDivElement);
}

myJsGraphics.prototype = jsGraphics.prototype;

Теперь вы сможете получить доступ к элементу, используя ваш новый конструктор:

var obj = new myJsGraphics(document.getElementById('blah-elem'));
elem = obj.canvasDiv;

Вся вещь замыкания немного странная, если вы к ней не привыкли, но суть в том, что функции, определенные в определенной области, но доступные в другом месте, могут ссылаться на переменные в области, в которой они были определены всегда. Самый простой пример - когда у вас есть функция, которая возвращает функцию:

function makeIncrementer(start) {
  return function () { return start++; };
}

var inc = makeIncrementer(0);
var inc2 = makeIncrementer(0);
inc(); // => 0
inc(); // => 1
inc(); // => 2
inc2(); // => 0

Эта ссылка на переменную «start» «закрывается», когда функция возвращается из функции makeIncrementer. К нему нельзя получить доступ напрямую. То же самое происходит в конструкторе объекта, где локальные переменные «закрыты» в функциях-членах объекта и действуют как частные переменные. Если в конструкторе не определена ссылка на метод или переменную для закрытого члена, вы просто не сможете получить к нему доступ.

2 голосов
/ 29 октября 2009

Эта техника "частного государства" становится все более идиоматичной в последние несколько лет. Лично я нашел это странным ограничением при попытке быстро отладить что-то с консоли или переопределить поведение в сторонней библиотеке. Это один из немногих случаев, когда я думаю: «Черт, почему я не могу сделать это с помощью языка». Я использовал эту ошибку в Firefox 2 для хорошего эффекта, когда мне действительно нужно было быстро отладить "приватную переменную".

Мне было бы интересно узнать, когда другие используют эту идиому или когда они ее избегают. (@bobince Я смотрю на тебя).

В любом случае @bobince в значительной степени ответил на ваш вопрос (вкратце: нет, вы не можете получить доступ к переменной canvasDiv из области, в которой находитесь).

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

Hack 1 : если вы сами управляете объектами:

var canvas = document.getElementById('canvas');
var gr = new jsGraphics(canvas);
gr._canvasDiv = canvas; // Augment because we'll need this later

// Sometime later...

gr._canvasDiv; // do something with the element

Если библиотека поддерживает какую-то концепцию, похожую на деструктор (запускается при выгрузке страницы или чего-то в этом роде), не забудьте также обнулить или удалить ваше свойство во избежание утечек памяти в IE:

delete gr._canvasDiv;

ИЛИ Hack 2 : перезаписать конструктор сразу после включения библиотеки:

// run after including the library, and before
// any code that instantiates jsGraphics objects
jsGraphics = (function(fn) {
    return function(canvas) {
        this._canvasDiv = canvas;
        return fn.apply(this, arguments)
    }
}(jsGraphics))

Затем получите доступ к элементу (теперь общедоступному) как gr._canvasDiv. То же самое относится и к удалению при выгрузке страницы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...