6

How do I maintain scope with this?

Original

var Base = new function() {

    var canvas;
    var context;

    this.init = function()
    {
        console.log("Canvas: " + $("canvas")[0])

        this.canvas = $("canvas")[0];
        console.log("Context: " + this.canvas.getContext('2d'))
        this.context = this.canvas.getContext('2d');

        $(window).resize(handleWindowResize);

        handleWindowResize();
    };

    function handleWindowResize()
    {
        console.log("Resize Canvas [" + this.canvas + "] to {width: " +
            $(window).width() + "," + $(window).width() + "}");
        this.canvas.width = $(window).width();
        this.canvas.height = $(window).height(); 
    }
}

$(window).load(function() { new Base.init() });

Ouput:

Canvas: [object HTMLCanvasElement]
Context: [object CanvasRenderingContext2D]
Resize Canvas [undefined] to {width: 1680,1680}
Resize Canvas [undefined] to {width: 1680,1680}

Revised

var Base = function() {
  this.canvas;
  this.context;
}
Base.prototype = {
  init: function()
  {
    console.log("init :: " + this);

    this.canvas = $('canvas')[0];
    this.context = this.canvas.getContext('2d')

    $(window).resize(this.handleWindowResize);
    this.handleWindowResize(null);
  },

  handleWindowResize: function()
  {
    console.log($(window) + " resized set canvas (" + this.canvas + ")" +
        " width,height = " + $(window).width() + "," + $(window).height());
  },

  toString: function()
  {
    return "[Base]";
  }
}

$(window).load(function() { var c = new Base(); c.init(); });

Output: (init)

init :: [Base]
[object Object] resized set canvas ([object HTMLCanvasElement]) width,height = 1659,630

Output: (on window resize)

[object Object] resized set canvas (undefined) width,height = 1658,630
cynicaljoy
  • 2,047
  • 1
  • 18
  • 25

5 Answers5

4

Here's an example of the Module pattern at work:

<html>
<head>

    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript">

        var Base = function() {

            //Private Variables
            var canvas;
            var context;

            //Private Function
            function handleWindowResize()
            {
                console.log("Resize Canvas [" + canvas + "] to {width: " + $(window).width() + "," + $(window).width() + "}");
                canvas.width = $(window).width();
                canvas.height = $(window).height(); 
            }

            //Public Functions
            return {

                init: function()
                {
                    console.log("Canvas: " + $("canvas")[0])

                    canvas = $("canvas")[0];
                    console.log("Context: " + canvas.getContext('2d'))
                    context = canvas.getContext('2d');

                    $(window).resize(handleWindowResize);

                    handleWindowResize();
                }

            };

        }();

        $(window).load(function() { Base.init() });

    </script>

</head>
<body>

    <canvas></canvas>

</body>
</html>
attack
  • 1,523
  • 12
  • 17
3

jQuery changes what object "this" refers to in its callback method. What you should do is store a reference of your objects "this" reference at the beginning:

var Base = new function() {
var self = this;
var canvas;
var context;

And then where you want to refer to the base object use self.

Matthew Manela
  • 16,572
  • 3
  • 64
  • 66
  • I don't know who voted your suggestion down, but this is the only one that has actually worked so far. Thanks! – cynicaljoy Jul 24 '10 at 01:32
1

Use: handleWindowResize.call(this) Instead Of: handleWindowResize()

This will change the scope.

Maz
  • 3,375
  • 1
  • 22
  • 27
1

It looks like you're coming from another language and need to get a solid idea of how constructors work in JS.

You have this.name=function(){} for one function, and a function declaration for another. Is there a reason you're using new function for the Base object? Executing new Base.init() doesn't seem quite right, either. The constructor is creating a new object, but you're discarding it, and only using the constructor to execute imperative code? You're not executing Base, so it could just be an object literal.

I really suggest checking out Doug Crockford's articles on inheritance and objects in JS. This SO Question talks about some other object creation techniques. Try searching for Crockford's The Good Parts. What is happening in Crockford's object creation technique?

I discarded the canvas stuff since I haven't worked with canvas. This will execute without errors.

<html><head>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> 
  <script>
   var Base = {
      canvas:null,
      context:null,

      init:function()
         {
         console.log("Canvas: " + $("#canvas")[0]);

         this.canvas = $("#canvas")[0];
         console.log("Context: " + this.canvas)
         //this.context = this.canvas.getContext('2d');
         this.handleWindowResize=function(){
         console.log("Resize Canvas [" + this.canvas + "] to {width: " + $(window).width() + "," +     $(window).width() + "}");
         this.canvas.style.width = $(window).width();
         this.canvas.style.height = $(window).height();
         }
        } 
      };


      $(window).load(function() { var c= new Base.init(); c.handleWindowResize()});
    </script>
  </head>

  <body>
    <div id='canvas' style='width:400px;background-color:red;height:30px;'>a div called Canvas</div>
  </body>
</html>
Community
  • 1
  • 1
Cole
  • 1,483
  • 1
  • 11
  • 20
  • While this does work and even being a hack matthew-manela's answer still provides me with the desired effect. I don't want to have a single bloated ` init()` method. I can see where you are going and I like the principal I just don't know how to get it to work exactly. And it probably does resolve around improper constructor. – cynicaljoy Jul 24 '10 at 02:05
  • @cynicaljoy I think you're being too hard on yourself (context in JS is difficult), and the use of var self = this is commonplace, not hacky at all. Check out this post, which shows the pattern Cole is suggesting but without the all-encompassing init function: http://www.wait-till-i.com/2007/07/24/show-love-to-the-module-pattern/ – attack Jul 24 '10 at 02:18
  • Well now I am onto something like what Cole suggested but I can't get this.canvas to return inside of the ` handleWindowResize` method when being triggered by the event listener. If I use ` this.handleWindowResize()` then it works. – cynicaljoy Jul 24 '10 at 02:46
  • @cynicaljoy you can always edit your question and paste in more code if you need to. And yeah, using `var self=this` is common in JS, that is a good way to go. I still wouldn't work with `new function`, and the mixed style of function assignment, since it's confusing, and I think the way you were doing 1/2 OO, 1/2 imperative makes as much sense as a large init function. Whatever works for you, of course! – Cole Jul 24 '10 at 05:18
  • @Cole I added a 'Revised' section to the Question, I would like to figure out how to use it this way rather than keep it as is because this is more readable. – cynicaljoy Jul 24 '10 at 07:10
0

Your code has several syntax errors. If handleWindowResize is a method within your Base object, then you should declare it similar to this.init and call it as this.handleWindowResize.

Jason McCreary
  • 71,546
  • 23
  • 135
  • 174
  • The codes doesn't have syntax errors. It may have poor formatting, but in either case when declaring such a statement a more _helpful_ thing would be to point out what that might be rather than just making a statement. The mechanic doesn't tell you "There is something with with the car." when he gives you the keys back. At least not a good one. – cynicaljoy Jul 24 '10 at 01:30
  • No syntax errors. I think the intent was to create a private function. (Right?) – attack Jul 24 '10 at 01:34
  • @cynicaljoy, your original post had syntax errors. I tried it on www.jsfiddle.net. I did make suggestions. Furthermore, the mere fact that I provided an answer shows my intention to be *helpful*. Unfortunately, it seems like this wasn't up to your standard. – Jason McCreary Jul 24 '10 at 03:13
  • my original post is still the one at the top, hasn't changed. The new revised one I just posted 15 minutes ago based off the suggestions of Cole. Here is the changes you suggested http://pastebin.com/Rq0v5LC8 - please put that into jsfiddle.net and try it. Because here is what I get when I test in Chrome 6, Safari 5, and Firefox 3.6.7 -> ` Uncaught TypeError: Object [object Object] has no method 'handleWindowResize'` – cynicaljoy Jul 24 '10 at 07:26