javascript using 'this' in global object

2019-06-04 06:03发布

问题:

What does 'this' keyword refer to when used in gloabl object?

Let's say for instance we have:

  var SomeGlobalObject =
  {
     rendered: true,
     show: function()
     {
        /*
        I should use 'SomeGlobalObject.rendered' below, otherwise it 
        won't work when called from event scope.
        But it works when called from timer scope!!
        How can this be?
        */
        if(this.rendered)
           alert("hello");
     }
  }

Now if we call in an inline script in the HTML page:

SomeGlobalObject.show();
window.setTimeout("Msg.show()", 1000);

everything work ok.

But if we do something like

AppendEvent(window, 'load', Msg.show);

we get an error because this.rendered is undefined when called from the event scope.

  1. Do you know why this happens?
  2. Could you explain then if there is another smarter way to do this without having to rewrite every time "SomeGlobalObject.someProperty" into the the SomeGlobalObject code?

Thanks!

AppendEvent is just a simple cross-browser function to append an event, code below, but it does not matter in order to answer the above questions.

  function AppendEvent(html_element, event_name, event_function)
  {
        if(html_element.attachEvent) //IE
           return html_element.attachEvent("on" + event_name, event_function);
        else
           if(html_element.addEventListener) //FF
              html_element.addEventListener(event_name, event_function, false);
  }

回答1:

When you reference a function that is a method of an object, you're detaching it from that object and this will no longer be a reference to the object.

The easiest solution is to wrap it in an anonymous function:

AppendEvent(window, 'load', function () { Msg.show() } );

There's also the bind method available to functions in ECMAScript 5th Edition implementations, which allow you to do this:

AppendEvent(window, 'load', Msg.show.bind(Msg, arg1, arg2));

The JS framework, Prototype, provides this method to current JS implementations too. The code (thanks @bobince):

// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
  Function.prototype.bind = function(){ 
    var fn = this, args = Array.prototype.slice.call(arguments),
        object = args.shift(); 
    return function(){ 
      return fn.apply(object, 
        args.concat(Array.prototype.slice.call(arguments))); 
    }; 
  };
}


回答2:

Do this

var SomeGlobalObject =
  {
     ...
  }

AppendEvent(window, 'load', function(){
    SomeGlobalObject.show();
});

Javascript supports dynamic scoping. So SomeGlobalObject will be available to the function declared inline always.



回答3:

The this keyword always refers to the calling object. In the first example, SomeGlobalObject is the caller.

I believe you would need to do something like AppendEvent(window, 'load', function() { SomeGlobalObject.show() })



回答4:

A simple way to describe what happened:

'this' always refers to the invoker of the function.

So take the offending case:

AppendEvent(window, 'load', Msg.show);

Event handling is invoked by the window. So 'this' becomes window and

window.rendered

is undefined.

bind() will quickly become your best friend :-)

As an aside

window.setTimeout("Msg.show()", 1000);

will run a little quicker if you supply the function object directly

window.setTimeout(Msg.show, 1000);

This is because the first syntax requires eval() of the string - basically compiling - before it can be invoked