0

I have the following Javascript function:

function MyPseudoClass(){

     this.initConfirmationSheet = function initConfirmationSheet(myDiv){
         //foo
         //bar
         //etc.
      }


      //foo
     function setUpGUI(){
     var myDiv = document.getElementById(DivForDiagram);
     myDiv.innerHTML = ""; //Clear contents of DIV for subsequent Diagram drawing. 
     confSheetDiagramRef = GO(go.Diagram, DivForDiagram,  // the DIV HTML element
    {
      initialContentAlignment: go.Spot.Center,
      initialAutoScale:go.Diagram.Uniform,
    "toolManager.mouseWheelBehavior": go.ToolManager.WheelZoom
    });
    confSheetDiagramRef.initialContentAlignment = go.Spot.Center;
    confSheetDiagramRef.undoManager.isEnabled = true;
    confSheetDiagramRef.isReadOnly = true;


    var texttemplate = GO(go.Node, "Horizontal", 
                      GO(go.TextBlock,
                                                new go.Binding("text", "text"), 
                                                new go.Binding("font", "fontType"),
                                                    new go.Binding("textAlign", "textAlign")
                                                ),
                                                    new go.Binding("location", "shapePosition"),
                                                    new go.Binding("angle", "rotation"),
                                                  new go.Binding("width", "width"), 
                                                  new go.Binding("height", "height")

                                            )


            var imagetemplate = GO(go.Node, "Horizontal", 
                      GO(go.Picture,
                                                new go.Binding("text", "text"), 
                                                new go.Binding("font", "fontType"), 
                                                  new go.Binding("location", "position"),
                                                    new go.Binding("angle", "rotation"),
                                                  new go.Binding("width", "width"),
                                                    new go.Binding("source", "text"),
                                                  new go.Binding("height", "height"),
                                                    new go.Binding("maxSize", "bounds")
                                                )
                                            )


var templmap = new go.Map("string", go.Node);
templmap.add("text", texttemplate);
templmap.add("image", imagetemplate);
confSheetDiagramRef.nodeTemplateMap = templmap;

    confSheetDiagramRef.addDiagramListener('InitialLayoutCompleted', function(e){
          alert("in listener!");
        generateImages(700,100);                                                                                                                                                                        
    })
    }

     function generateImages(){
          //foo
          //bar
          this.initConfirmationSheet('myDiv');
      }

 }

MyPseudoClass is called in the following context:

    nextConf = new MyPseudoClass(this);
            nextConf.setInfo(confInfoVO, true, PasseDInEventDateTime_str, ParentRef.TicketPrintLayout, false);
            nextConf.printAllConfirmations(ParentRef.DivName, numConfirmations);

And I get the following error: TypeError: this.initConfirmationSheet is not a function

I am not sure why the first function is not in the scope of the second. Especially because the first function is available to another function within MyPseudoClass:

this.thirdFunction = function thirdFunction(divName){
    this.initConfirmationSheet(divName);
 }

Is it because this function is defined, or attached to the instance of this class? as opposed to the other which is not defined with this.functionName?

Thalatta
  • 4,510
  • 10
  • 48
  • 79
  • 3
    where/how is `generateImages` called? – Daniel A. White Oct 20 '15 at 22:34
  • 2
    Your example is incomplete and does not properly demonstrate the problem you claim. – Travis J Oct 20 '15 at 22:36
  • 2
    Please show how you call `MyPseudoClass()` and how you call `generateImages()`. Given that `generateImages()` is *not* an instance method of any instances of `MyPseudoClass()`, within it `this` will not refer to the current instance either (unless you went out of your way to make it happen with `.call()` or `.apply()` or `.bind()`), so... – nnnnnn Oct 20 '15 at 22:38
  • It may have something to do with the fact that I call `initConfirmationSheet` within an event listener, which may have different scoping properties. – Thalatta Oct 20 '15 at 22:47
  • Possible duplicate of [How do JavaScript closures work?](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – Kyll Oct 21 '15 at 00:28

2 Answers2

2

You are not bound to the MyPseudoClass when you call from exterior sources for instances <a onclick="myPseudoClassObj.generateImages()>, you can force a binding on all calls by doing

 this.generateImages = function(){
      //foo
      //bar
      this.initConfirmationSheet('myDiv');
  }.bind(this);

see fiddle

Victory
  • 5,811
  • 2
  • 26
  • 45
1

In JavaScript, this has a different meaning than in other languages. It depends on the context of who is calling it. For example, if generateImages() is called in a callback function to an asynchronous request, this will refer to the request object itself, and thus, in that scope initConfirmationSheet is not defined.

You have at least 2 different solutions for this:

  1. Use the old-school and mostly frowned upon method of creating what is known as a closure keeping a reference to the this object, by doing something like

     var that = this;
    

    Which will create a scoped variable named that, holding the value of this at the time it was called. Be wary of the fact that race conditions can happen when the value changes before you expect it to.

  2. The cleaner method is to use ES5's .bind(this) on a function to create a copy of that function bound to the current this object. For example, if you need to call generateImages() on success of a GET request in jQuery:

     $.get("/something", {}, generateImages.bind(this));
    

    You can now freely use this.initConfirmationSheet('myDiv') as this will be bound to the right object.

The Mozilla Developper Network has a very detailed explanation of the meaning of this in JavaScript, and I recommend you read it as it's a core element of the language:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#The_bind_method

A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.

In most cases, the value of this is determined by how a function is called. It can't be set by assignment during execution, and it may be different each time the function is called. ES5 introduced the bind method to set the value of a function's this regardless of how it's called.

Another good resource to get you started would be to read JavaScript: The Good Parts as it does a terrific job at explaining the subtleties of the language.

Community
  • 1
  • 1