Javascript - get a variable from a local area of ​​a function

I don't really like anything other than basic javascript, so please forgive a simple question.

I am using jsDraw2D library. This library has a graphic object that looks something like this:

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

You use it as follows:

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

I would like to add a function in jsGraphics so that I can call

 gr.getCanvasElement() 

Is there any way to do this without editing the library itself?

I tried

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

but this does not seem to work. I have an intuitive feeling that its something with this new keyword, but if you could explain why this is not so, it will also be useful.

0
source share
3 answers

You cannot just get the canvasDiv element necessarily, because if it is never assigned to an object in the constructor using this keyword, a link to this object exists in the closure created by the constructor function itself.

However, you can wrap the constructor in a new constructor, and then set the prototypes equal:

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

Now you can access the element using the new constructor:

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

The whole closing thing is a little strange if you are not used to it, but the point is that functions defined in a certain area, but available in other places, can refer to variables in the area in which they were defined at any time. The simplest example is when you have a function that returns a function:

 function makeIncrementer(start) { return function () { return start++; }; } var inc = makeIncrementer(0); var inc2 = makeIncrementer(0); inc(); // => 0 inc(); // => 1 inc(); // => 2 inc2(); // => 0 

This reference to the "start" variable is "closed" when the function returns from the makeIncrementer function. Access to it is directly impossible. The same thing happens in the constructor of the object, where local variables are "closed" in the member functions of the object and act as private variables. If there is no method or variable reference for the private member defined in the constructor, you simply cannot access it.

+3
source

No, this does not use normal JavaScript prototype-based inheritance, adding a separate drawLine function to each jsGraphics instance, each with a closure around its own canvasDiv variable.

Once function jsGraphics() { closed } , there is no other way to access the canvasDiv variable at all, unless one of the functions inside provides access to it. This is often done intentionally to make private variables explicitly to stop you on canvasDiv.

+4
source

This technique of "private state" in recent years has become increasingly idiomatic. Personally, I found that this strangely limits the attempt to quickly debug something from the console or override the behavior in a third-party library. This is one of the few times that I think, “Damn it, why I can't do this with the language.” I used this error in Firefox 2 for a good effect when I really need to quickly debug a "private variable".

I would be interested to know when others use this idiom or when they avoid it. (@bobince, I look at you).

Anyway, @bobince pretty much answered your question (nutshell: No, you cannot access the canvasDiv variable from the area you are in).

However, there is one thing you can do is the trade-off between hacking or editing a third-party library (I always go for hacking;): you can enlarge the object to keep the link that you know you need later.

Hack 1 : if you manage object instances yourself:

 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 

If the library supports some concept similar to a destructor (it is activated when a page is unloaded or something like that), be sure to remove or delete your property there to avoid memory leaks in IE:

 delete gr._canvasDiv; 

OR Hack 2 : Overwrite the constructor immediately after turning on the library:

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

Then select the item (now public) as gr._canvasDiv . This is the same remark about deleting it on page unloading.

+2
source

All Articles