开发者

In Javascript, confusion with "this" - how do you make this _this_ when method invoked by an event?

开发者 https://www.devze.com 2023-02-27 17:14 出处:网络
With a sample "class": var Klass = { \'name\': 24, \'init\': function() { var input1 = document.getElementsByTagName(\'input\')[0];

With a sample "class":

var Klass = {
 'name': 24,
 'init': function() { 
            var input1 = document.getElementsByTagName('input')[0];
            input1.addEventTrigger('change',func1,true);
          },
 'func1': function( arg ) {
            // func called by an event
            if( arg instanceof Event ) {
               console.log( this.name );
               arg.preventDefault();
            }
            // func called "normally"
            else console.log( this.name );
           }
}

to the best of my understanding:

  • if Klass.func1 is called within the normal context of the script, 24 will be written to the console, because Klass is _this_
  • if <closure>.func1 is called by the input item's onchange trigger, the input's name will be written to the console, because the element the trigger is attached to is _this_

if correct, to me this seems somewhat counter-intuitive for this to be context开发者_运维问答 sensitive.

this leads to two questions:

  1. is the laid out scenario correct?
  2. if func1 is called by an Event, how do I move the closure (or scope(?)) around so that _this_ is original Klass instead of the Event.target?


  1. If relative to your understanding, then yes.

  2. var Klass = { '$this': this, 'name': 24, ... Then use $this inside of func1

You can also define Klass as an instance inside of a closure rather then using object notation so that your $this instance is private when using var $this = this;

EDIT: $this in this case would be an instance of DOMWindow


The concept of 'this' is one of the more misunderstood aspects of JavaScript. There have been many a StackOverflow question that discusses the concept of 'this', as well as numerous blog posts, articles, etc. I won't claim to be an expert, but I believe I can clear some of the muddy water evident in your question.

The value of 'this' is based on the context in which the current function is being executed. Consider the following code:

this; // Alone, will be 'window' or DOMWindow

Now, in the context of a simple object:

var Klass = { 
   self: this // -> DOMWindow
}

If we were to examine Klass.self, we would find that it is still DOMWindow, and not, in fact, Klass. This is because the value of 'this' was evaluated at the time that Klass was constructed, and it was constructed at the top level in the context of DOMWindow.

Another example:

var Klass = { 
   func: function(){
       console.log(this);
   }
}
Klass.func(); // -> Klass

Now, if we take a look at the value of 'this' from the execution of Klass.func(), it will in fact be Klass. This is because func was executed in the scope of Klass. Another example:

var Klass = { 
   func: function(){
       console.log(this);
   }
}

var anotherFunc = Klass.func;
anotherFunc(); // -> this is now DOMWindow

anotherFunc is a reference to Klass.func, but not in the context of Klass. We just used Klass to get a handle on func, but once we have that handle, anotherFunc has no concept of Klass anymore. This is the same as happens with event handling - often you pass it the handle to the function you want executed. How you got that handle is irrelevant.

var Klass = { 
   func: function(){
       console.log(this);
   }
}

var anotherFunc = Klass.func;
anotherFunc.call(Klass); //-> this is now Klass again!

In the above example, we execute anotherFunc using the 'call' function. This is a way to force execution context. If we wanted to pass arguments to the function, you could still do so:

anotherFunc.call(Klass, someArgument);

There are many ways to deal with context and 'this' in JavaScript. The above examples mainly deal with basic 'objects'. You get a few more options on how to handle things if your object is, itself, a function. But we'll leave that be for now! As for your example, you could get the desired behavior like so:

var Klass = {
    'name': 24,
    'init': function() { 
        var input1 = document.getElementsByTagName('input')[0];
        input1.addEventTrigger('change',Klass.func1,true); // note the addition of Klass to the func1 reference here.  Because just 'func1' is undefined in the scope otherwise.
    },
    'func1': function( arg ) {
        console.log(Klass.name );

        if( arg instanceof Event ) {
            arg.preventDefault();
        }
    }
}

EDIT: In response to your comment:

 addEventTrigger('change',Klass.change.call(Klass),true) 

That will call Klass.change right away, instead of during the callback process. Instead, the callback will be assigned to the result of Klass.change, which is not your desired behavior. Instead, wrap it in an anonymous function:

 addEventTrigger('change', function(){
     Klass.change();
 }, true);

Notice I'm not having to use call() there, because I am actually executing it from Klass, thus giving it a Klass execution context. You can call Klass.change() from anywhere, and the value of 'this' inside of change() will be Klass. The difference between calling it directly, and using it to pass the function handle to the callback is subtle, but just remember that 'this' is all about when and how the function actually gets executed, not assigned or passed around.

Google javascript this site:stackoverflow.com for lots more great questions on this topic, many of which include great external links.


Either change your callback to function() { Klass.func1(event) }

Or use wrapper will keep the context (ie this) of a function when called as a callback.

function wrapper(callback, context) {
    return function() {return callback.apply(context, arguments);}
}

Example:

// Simple function
echo = function bob(msg){return this.greeting + " " + msg;}

// Wrap two functions
var c1 = wrapper(echo, {greeting: "Hello"});
var c2 = wrapper(echo, {greeting: "Goodbye"});

console.log(c1("Bob"));
// Hello Bob
console.log(c2("Sam"));
// Goodbye Bob
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号