SitePoint Sponsor

User Tag List

Results 1 to 8 of 8

Hybrid View

  1. #1
    SitePoint Guru Ize's Avatar
    Join Date
    Nov 2005
    Location
    The Netherlands
    Posts
    808
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Losing object-reference when invoking global function

    If I create an object and invoke a global function from within the object, I lose the scope of the object when I return to it.

    I'm having a bit of trouble explaining it, so I'll just show you the code:

    Code:
    function Foo (id)
    {
    	this.theObj = document.getElementById(id);
    		
    	this.makeMePretty = function ()
    	{
    		setBg (this.theObj, this.setBorder);
    	}
    		
    	this.setBorder = function ()
    	{
    		this.theObj.style.border = '5px dashed red';
    	}
    		
    	this.makeMePretty ();
    }
    	
    function setBg (elm,fn)
    {
    	elm.style.backgroundColor = 'yellow';
    	fn();
    }
    	
    window.onload = function ()
    {
    	var a = new Foo ('foo');
    }
    The global function (setBg) successfully executes and does recognize the correct scope. It executes Foo.setBorder, but inside setBorder, the object is lost. The Firefox Javascript debugger gives me the following information:

    Code:
    this.theObj has no properties
    ...and when I do alert(this); inside setBorder, it returns "object Window".

    How can I maintain the reference to my object and still invoke the global function?

  2. #2
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Ize,

    This was part of my response to someone else's question but I think it will help here.
    Code:
    function Widget() // object prototype
    {
      this.id = '';
      this.myfunc = function()
      {
      };  
      setTimeout(this.myfunc, 1000);
    }
    In the above code it appears that we want "this.myfunc" to be called after 1 second. And it does indeed get called after 1 sec. However, when it is called the "this" variable does not point to the proper object instance as we might think it should. It probably will point to the window object.

    When we pass "this.myfunc" as the first argument to setTimeout, we are only passing a reference to the function. There is no information passed that would associate myfunc with "this" instance object.

    Before the Javascript interpreter "calls" any function, it first sets the value of the "this" variable. What does it get set it to? For example:
    Code:
    // If the calling expression is like this:
    myobj.func();
    // then "this" will be assigned a reference to "myobj" before "func" is called.
    
    // If the calling expression is like this:
    func();
    // then "this" will be assigned a reference to the "window" object (the "global" object) before "func" is called.

  3. #3
    SitePoint Guru Ize's Avatar
    Join Date
    Nov 2005
    Location
    The Netherlands
    Posts
    808
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That's a clear explanation, thanks

    Is it required to send "this" from the object to the global function, then, to provide a scope in which the method should be invoked?

    I would prefer to do it the other way around, invoke the global function as if it was a method of the current object. I believe that's possible using call () and/or apply (), but I don't completely understand how that works..

  4. #4
    SitePoint Guru Ize's Avatar
    Join Date
    Nov 2005
    Location
    The Netherlands
    Posts
    808
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I ran another test, using the following code:

    Code:
    // inside the object:
    setBg (this.theObj, this.setBorder, this);
    
    // ---------------------
    // the new global setBg: 
    
    function setBg (elm,fn,scope)
    {
    	elm.style.backgroundColor = 'yellow';
    	scope.fn();
    }
    This'll yield "scope.fn is not a function"..

  5. #5
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ize View Post
    This'll yield "scope.fn is not a function"..
    That's because you're calling to a function named "fn" on the object. The common solution to your problem is to create an anonymous function:

    Code:
    Foo.prototype.makeMePretty = function() {
      var self = this;
      setBg(this.theObj, function() {
        self.setBorder();
      });
    }
    This is a very common pattern in javascript. Most libraries has some sort of shortcut to it, such as MochiKit.bind.

  6. #6
    SitePoint Guru Ize's Avatar
    Join Date
    Nov 2005
    Location
    The Netherlands
    Posts
    808
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks, Kyber, that I can work with

  7. #7
    SitePoint Wizard Pepejeria's Avatar
    Join Date
    Jan 2005
    Location
    Too far up north
    Posts
    1,566
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    An alternative solution (that requires the global function to be changed though) would be the following
    Code:
    function Foo (id) {
    	this.theObj = document.getElementById(id);
    
    	this.makeMePretty = function () {
    		setBg.call(this, this.theObj, this.setBorder);
    	}
    
    	this.setBorder = function () {
    		this.theObj.style.border = '5px dashed red';
    	}
    
    	this.makeMePretty ();
    }
    
    function setBg (elm,fn) {
    	elm.style.backgroundColor = 'yellow';
    	fn.call(this);
    }
    
    window.onload = function () {
    	var a = new Foo ('myWidget');
    }
    kyberfabrikken's solution is what I would recommend though.
    Last edited by Pepejeria; Apr 6, 2007 at 15:21.

  8. #8
    I'll take mine raw silver trophy MikeFoster's Avatar
    Join Date
    Dec 2002
    Location
    Alabama, USA
    Posts
    2,560
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Dang, we're good!


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •