How to change the context of a function in javascr

2020-02-08 04:45发布

问题:

I'm trying to understand why in javascript, you might want to change the context of a function. I'm looking for a real world example or something which will help me understand how / why this technique is used and what its significance is.

The technique is illustrated using this example (from http://ejohn.org/apps/learn/#25)

var object = {}; 
function fn(){ 
  return this; 
} 
assert( fn() == this, "The context is the global object." ); 
assert( fn.call(object) == object, "The context is changed to a specific object." );

回答1:

jQuery makes use of it to good effect:

$('a').each(function() {
    // "this" is an a element - very useful
});

The actual jQuery code looks like this:

for ( name in object ) {
    if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
        break;
    }
}

If it just did callback( name, object[ name ] ) then this wouldn't be set to the current object in your iterator and you'd have to use the parameter instead. Basically it just makes things easier.



回答2:

Please have a look at this example:

<script>
var el = document.getElementById('button');
el.onclick = function(){
    this.value = "Press Me Again"; //this --> now refers to the the element button not on the window
}

//Another Example:
var Person = function(name,location){
  this.name = name;
  this.location = location;  
  alert(this.location); 
}   
var p2 = new Person("Samantha","California"); //this refers to the instance of the function Person(Person now acts as a class)
var p1 = Person(); // this refers to the window(Person simply acts as a simple function)
</script>
<button id="button1">Press Me</button>

The new keyword changes the context.



回答3:

It's very useful when doing callbacks from AJAX requests:

function Person(_id, _name) {
    this.id = _id;
    this.name = _name;
};

Person.prototype.sayHi = function(greeting) {
    alert(greeting + " from " + this.name);
};

Person.prototype.loadFromAJAX = function(callback) {
    // in this example, it's jQuery, but could be anything
    var t = this;
    $.get("myurl.php", function(data) {
        callback.call(t, data.greeting);
    });
};

Actually, that's a pretty crappy example.

There are tons of uses of it in jQuery. For example, the jQuery().get() function:

get: function( num ) {
    return num === undefined ?
        // Return a 'clean' array
        Array.prototype.slice.call( this ) :
        // Return just the object
        this[ num ];
}

It's using the functions of the Array prototype but in the context of the jQuery object.



回答4:

A real world example that i've encountered:

If you add a function as an event handler to a DOM element and if you use "this" inside that function, "this" will refer to the DOM element that you added the event handler to.

But that function might be a method of an object and you want the "this" keyword used inside it to refer to the owner object...so you need to change the context so that "this" will not refer to the DOM element but will refer to the owner object.

You can easily change the context of a function in jquery using the proxy() function. See this question: jquery "this" binding issue on event handler (equivalent of bindAsEventListener in prototype) and the first answer



回答5:

bind function might be what you're looking for, bind function returns a new function with in the context that you passed in , a real world scenario could be when you are using jquery delegates to attach some behavior to a dom element, and you want the callback being execute in a different context. 'cause the default context in a jquery delgate is the dom object that is bound to the handler , which means you can't access any property besides the properties that belongs to the dom object



回答6:

I always find myself in the need of having different context when using setTimeout and jQuery has a handy function $.proxy which does the trick:

function iAmCalledAfterTimeout()
{
     alert(this.myProperty); //it will alert "hello world"
}    

setTimeout($.proxy(iAmCalledAfterTimeout, {myProperty:"hello world"}), 1000);