How does the “this” keyword work?

问题: I have noticed that there doesn't appear to be a clear explanation of what the this keyword is and how it is correctly (and incorrectly) used in JavaScript on the Stack Ove...

问题:

I have noticed that there doesn't appear to be a clear explanation of what the this keyword is and how it is correctly (and incorrectly) used in JavaScript on the Stack Overflow site.

I have witnessed some very strange behaviour with it and have failed to understand why it has occurred.

How does this work and when should it be used?


回答1:

I recommend reading Mike West's article Scope in JavaScript (mirror) first. It is an excellent, friendly introduction to the concepts of this and scope chains in JavaScript.

Once you start getting used to this, the rules are actually pretty simple. The ECMAScript 5.1 Standard defines this:

§11.1.1 The this keyword

The this keyword evaluates to the value of the ThisBinding of the current execution context

ThisBinding is something that the JavaScript interpreter maintains as it evaluates JavaScript code, like a special CPU register which holds a reference to an object. The interpreter updates the ThisBinding whenever establishing an execution context in one of only three different cases:

1. Initial global execution context

This is the case for JavaScript code that is evaluated at the top-level, e.g. when directly inside a <script>:

<script>
  alert("I'm evaluated in the initial global execution context!");

  setTimeout(function () {
      alert("I'm NOT evaluated in the initial global execution context.");
  }, 1);
</script>

When evaluating code in the initial global execution context, ThisBinding is set to the global object, window (§10.4.1.1).

Entering eval code

  • …by a direct call to eval() ThisBinding is left unchanged; it is the same value as the ThisBinding of the calling execution context (§10.4.2 (2)(a)).

  • …if not by a direct call to eval()
    ThisBinding is set to the global object as if executing in the initial global execution context (§10.4.2 (1)).

§15.1.2.1.1 defines what a direct call to eval() is. Basically, eval(...) is a direct call whereas something like (0, eval)(...) or var indirectEval = eval; indirectEval(...); is an indirect call to eval(). See chuckj's answer to (1, eval)('this') vs eval('this') in JavaScript? and Dmitry Soshnikov’s ECMA-262-5 in detail. Chapter 2. Strict Mode. for when you might use an indirect eval() call.

Entering function code

This occurs when calling a function. If a function is called on an object, such as in obj.myMethod() or the equivalent obj["myMethod"](), then ThisBinding is set to the object (obj in the example; §13.2.1). In most other cases, ThisBinding is set to the global object (§10.4.3).

The reason for writing "in most other cases" is because there are eight ECMAScript 5 built-in functions that allow ThisBinding to be specified in the arguments list. These special functions take a so-called thisArg which becomes the ThisBinding when calling the function (§10.4.3).

These special built-in functions are:

  • Function.prototype.apply( thisArg, argArray )
  • Function.prototype.call( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Function.prototype.bind( thisArg [ , arg1 [ , arg2, ... ] ] )
  • Array.prototype.every( callbackfn [ , thisArg ] )
  • Array.prototype.some( callbackfn [ , thisArg ] )
  • Array.prototype.forEach( callbackfn [ , thisArg ] )
  • Array.prototype.map( callbackfn [ , thisArg ] )
  • Array.prototype.filter( callbackfn [ , thisArg ] )

In the case of the Function.prototype functions, they are called on a function object, but rather than setting ThisBinding to the function object, ThisBinding is set to the thisArg.

In the case of the Array.prototype functions, the given callbackfn is called in an execution context where ThisBinding is set to thisArg if supplied; otherwise, to the global object.

Those are the rules for plain JavaScript. When you begin using JavaScript libraries (e.g. jQuery), you may find that certain library functions manipulate the value of this. The developers of those JavaScript libraries do this because it tends to support the most common use cases, and users of the library typically find this behavior to be more convenient. When passing callback functions referencing this to library functions, you should refer to the documentation for any guarantees about what the value of this is when the function is called.

If you are wondering how a JavaScript library manipulates the value of this, the library is simply using one of the built-in JavaScript functions accepting a thisArg. You, too, can write your own function taking a callback function and thisArg:

function doWork(callbackfn, thisArg) {
    //...
    if (callbackfn != null) callbackfn.call(thisArg);
}

There’s a special case I didn’t yet mention. When constructing a new object via the new operator, the JavaScript interpreter creates a new, empty object, sets some internal properties, and then calls the constructor function on the new object. Thus, when a function is called in a constructor context, the value of this is the new object that the interpreter created:

function MyType() {
    this.someData = "a string";
}

var instance = new MyType();
// Kind of like the following, but there are more steps involved:
// var instance = {};
// MyType.call(instance);

Arrow functions

Arrow functions (introduced in ECMA6) alter the scope of this. See the existing canonical question, Arrow function vs function declaration / expressions: Are they equivalent / exchangeable? for more information. But in short:

Arrow functions don't have their own this.... binding. Instead, those identifiers are resolved in the lexical scope like any other variable. That means that inside an arrow function, this...refer(s) to the values of this in the environment the arrow function is defined in.

Just for fun, test your understanding with some examples

To reveal the answers, mouse over the light yellow boxes.

  1. What is the value of this at the marked line? Why?

    window — The marked line is evaluated in the initial global execution context.

    if (true) {
        // What is `this` here?
    }
    
  2. What is the value of this at the marked line when obj.staticFunction() is executed? Why?

    obj — When calling a function on an object, ThisBinding is set to the object.

    var obj = {
        someData: "a string"
    };
    
    function myFun() {
        return this // What is `this` here?
    }
    
    obj.staticFunction = myFun;
    
    console.log("this is window:", obj.staticFunction() == window);
    console.log("this is obj:", obj.staticFunction() == obj);
      

  3. What is the value of this at the marked line? Why?

    window

    In this example, the JavaScript interpreter enters function code, but because myFun/obj.myMethod is not called on an object, ThisBinding is set to window.

    This is different from Python, in which accessing a method (obj.myMethod) creates a bound method object.

    var obj = {
        myMethod: function () {
            return this; // What is `this` here?
        }
    };
    var myFun = obj.myMethod;
    console.log("this is window:", myFun() == window);
    console.log("this is obj:", myFun() == obj);
      

  4. What is the value of this at the marked line? Why?

    window

    This one was tricky. When evaluating the eval code, this is obj. However, in the eval code, myFun is not called on an object, so ThisBinding is set to window for the call.

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        myMethod: function () {
            eval("myFun()");
        }
    };
    
  5. What is the value of this at the marked line? Why?

    obj

    The line myFun.call(obj); is invoking the special built-in function Function.prototype.call(), which accepts thisArg as the first argument.

    function myFun() {
        return this; // What is `this` here?
    }
    var obj = {
        someData: "a string"
    };
    console.log("this is window:", myFun.call(obj) == window);
    console.log("this is obj:", myFun.call(obj) == obj);
      


回答2:

The this keyword behaves differently in JavaScript compared to other language. In Object Oriented languages, the this keyword refers to the current instance of the class. In JavaScript the value of this is determined mostly by the invocation context of function (context.function()) and where it is called.

1. When used in global context

When you use this in global context, it is bound to global object (window in browser)

document.write(this);  //[object Window]

When you use this inside a function defined in the global context, this is still bound to global object since the function is actually made a method of global context.

function f1()
{
   return this;
}
document.write(f1());  //[object Window]

Above f1 is made a method of global object. Thus we can also call it on window object as follows:

function f()
{
    return this;
}

document.write(window.f()); //[object Window]

2. When used inside object method

When you use this keyword inside an object method, this is bound to the "immediate" enclosing object.

var obj = {
    name: "obj",
    f: function () {
        return this + ":" + this.name;
    }
};
document.write(obj.f());  //[object Object]:obj

Above I have put the word immediate in double quotes. It is to make the point that if you nest the object inside another object, then this is bound to the immediate parent.

var obj = {
    name: "obj1",
    nestedobj: {
        name:"nestedobj",
        f: function () {
            return this + ":" + this.name;
        }
    }            
}

document.write(obj.nestedobj.f()); //[object Object]:nestedobj

Even if you add function explicitly to the object as a method, it still follows above rules, that is this still points to the immediate parent object.

var obj1 = {
    name: "obj1",
}

function returnName() {
    return this + ":" + this.name;
}

obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1

3. When invoking context-less function

When you use this inside function that is invoked without any context (i.e. not on any object), it is bound to the global object (window in browser)(even if the function is defined inside the object) .

var context = "global";

var obj = {  
    context: "object",
    method: function () {                  
        function f() {
            var context = "function";
            return this + ":" +this.context; 
        };
        return f(); //invoked without context
    }
};

document.write(obj.method()); //[object Window]:global 

Trying it all with functions

We can try above points with functions too. However there are some differences.

  • Above we added members to objects using object literal notation. We can add members to functions by using this. to specify them.
  • Object literal notation creates an instance of object which we can use immediately. With function we may need to first create its instance using new operator.
  • Also in an object literal approach, we can explicitly add members to already defined object using dot operator. This gets added to the specific instance only. However I have added variable to the function prototype so that it gets reflected in all instances of the function.

Below I tried out all the things that we did with Object and this above, but by first creating function instead of directly writing an object.

/********************************************************************* 
  1. When you add variable to the function using this keyword, it 
     gets added to the function prototype, thus allowing all function 
     instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
    this.name = "ObjDefinition";
    this.getName = function(){                
        return this+":"+this.name;
    }
}        

obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition   

/********************************************************************* 
   2. Members explicitly added to the function protorype also behave 
      as above: all function instances have their own copy of the 
      variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
    return "v"+this.version; //see how this.version refers to the
                             //version variable added through 
                             //prototype
}
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   3. Illustrating that the function variables added by both above 
      ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
    this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1

obj2.incrementVersion();      //incrementing version in obj2
                              //does not affect obj1 version

document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1

/********************************************************************* 
   4. `this` keyword refers to the immediate parent object. If you 
       nest the object through function prototype, then `this` inside 
       object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj', 
                                    getName1 : function(){
                                        return this+":"+this.name;
                                    }                            
                                  };

document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj

/********************************************************************* 
   5. If the method is on an object's prototype chain, `this` refers 
      to the object the method was called on, as if the method was on 
      the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
                                    //as its prototype
obj3.a = 999;                       //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
                                    //calling obj3.fun() makes 
                                    //ProtoObj.fun() to access obj3.a as 
                                    //if fun() is defined on obj3

4. When used inside constructor function.

When the function is used as a constructor (that is when it is called with new keyword), this inside function body points to the new object being constructed.

var myname = "global context";
function SimpleFun()
{
    this.myname = "simple function";
}

var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
//   object being constructed thus adding any member
//   created inside SimipleFun() using this.membername to the
//   object being constructed
//2. And by default `new` makes function to return newly 
//   constructed object if no explicit return value is specified

document.write(obj1.myname); //simple function

5. When used inside function defined on prototype chain

If the method is on an object's prototype chain, this inside such method refers to the object the method was called on, as if the method is defined on the object.

var ProtoObj = {
    fun: function () {
        return this.a;
    }
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun() 
//to be the method on its prototype chain

var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999

//Notice that fun() is defined on obj3's prototype but 
//`this.a` inside fun() retrieves obj3.a   

6. Inside call(), apply() and bind() functions

  • All these methods are defined on Function.prototype.
  • These methods allows to write a function once and invoke it in different context. In other words, they allows to specify the value of this which will be used while the function is being executed. They also take any parameters to be passed to the original function when it is invoked.
  • fun.apply(obj1 [, argsArray]) Sets obj1 as the value of this inside fun() and calls fun() passing elements of argsArray as its arguments.
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - Sets obj1 as the value of this inside fun() and calls fun() passing arg1, arg2, arg3, ... as its arguments.
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - Returns the reference to the function fun with this inside fun bound to obj1 and parameters of fun bound to the parameters specified arg1, arg2, arg3,....
  • By now the difference between apply, call and bind must have become apparent. apply allows to specify the arguments to function as array-like object i.e. an object with a numeric length property and corresponding non-negative integer properties. Whereas call allows to specify the arguments to the function directly. Both apply and call immediately invokes the function in the specified context and with the specified arguments. On the other hand, bind simply returns the function bound to the specified this value and the arguments. We can capture the reference to this returned function by assigning it to a variable and later we can call it any time.
function add(inc1, inc2)
{
    return this.a + inc1 + inc2;
}

var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
      //above add.call(o,5,6) sets `this` inside
      //add() to `o` and calls add() resulting:
      // this.a + inc1 + inc2 = 
      // `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
      // `o.a` i.e. 4 + 5 + 6 = 15

var g = add.bind(o, 5, 6);       //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />");    //15

var h = add.bind(o, 5);          //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
      // 4 + 5 + 6 = 15
document.write(h() + "<br />");  //NaN
      //no parameter is passed to h()
      //thus inc2 inside add() is `undefined`
      //4 + 5 + undefined = NaN</code>

7. this inside event handlers

  • When you assign function directly to event handlers of an element, use of this directly inside event handling function refers to the corresponding element. Such direct function assignment can be done using addeventListener method or through the traditional event registration methods like onclick.
  • Similarly, when you use this directly inside the event property (like <button onclick="...this..." >) of the element, it refers to the element.
  • However use of this indirectly through the other function called inside the event handling function or event property resolves to the global object window.
  • The same above behavior is achieved when we attach the function to the event handler using Microsoft's Event Registration model method attachEvent. Instead of assigning the function to the event handler (and the thus making the function method of the element), it calls the function on the event (effectively calling it in global context).

I recommend to better try this in JSFiddle.

<script> 
    function clickedMe() {
       alert(this + " : " + this.tagName + " : " + this.id);
    } 
    document.getElementById("button1").addEventListener("click", clickedMe, false);
    document.getElementById("button2").onclick = clickedMe;
    document.getElementById("button5").attachEvent('onclick', clickedMe);   
</script>

<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>

<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />

<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />

IE only: <button id="button5">click() "attached" using attachEvent() </button>

回答3:

Javascript's this

Simple function invocation

Consider the following function:

function foo() {
    console.log("bar");
    console.log(this);
}
foo(); // calling the function

Note that we are running this in the normal mode, i.e. strict mode is not used.

When run in a browser, the value of this would be logged as window. This is because window is the global variable in a web browser's scope.

If you run this same piece of code in an environment like node.js, this would refer to the global variable in your app.

Now if we run this in strict mode by adding the statement "use strict"; to the beginning of the function declaration, this would no longer refer to the global variable in either of the envirnoments. This is done to avoid confusions in the strict mode. this would, in this case just log undefined, because that is what it is, it is not defined.

In the following cases, we would see how to manipulate the value of this.

Calling a function on an object

There are different ways to do this. If you have called native methods in Javascript like forEach and slice, you should already know that the this variable in that case refers to the Object on which you called that function (Note that in javascript, just about everything is an Object, including Arrays and Functions). Take the following code for example.

var myObj = {key: "Obj"};
myObj.logThis = function () {
    // I am a method
    console.log(this);
}
myObj.logThis(); // myObj is logged

If an Object contains a property which holds a Function, the property is called a method. This method, when called, will always have it's this variable set to the Object it is associated with. This is true for both strict and non-strict modes.

Note that if a method is stored (or rather, copied) in another variable, the reference to this is no longer preserved in the new variable. For example:

// continuing with the previous code snippet

var myVar = myObj.thisMethod;
myVar();
// logs either of window/global/undefined based on mode of operation

Considering a more commonly practical scenario:

var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself

The new keyword

Consider a constructor function in Javascript:

function Person (name) {
    this.name = name;
    this.sayHello = function () {
        console.log ("Hello", this);
    }
}

var awal = new Person("Awal");
awal.sayHello();
// In `awal.sayHello`, `this` contains the reference to the variable `awal`

How does this work? Well, let's see what happens when we use the new keyword.

  1. Calling the function with the new keyword would immediately initialze an Object of type Person.
  2. The constructor of this Object has its constructor set to Person. Also, note that typeof awal would return Object only.
  3. This new Object would be assigned the protoype of Person.prototype. This means that any method or property in the Person prototype would be available to all instances of Person, including awal.
  4. The function Person itself is now invoked; this being a reference to the newly constructed object awal.

Pretty straighforward, eh?

Note that the official ECMAScript spec no where states that such types of functions are actual constructor functions. They are just normal functions, and new can be used on any function. It's just that we use them as such, and so we call them as such only.

Calling functions on Functions : call and apply

So yeah, since functions are also Objects (and in-fact first class variables in Javascript), even functions have methods which are... well, functions themselved.

All functions inherit from the global Function, and two of its many methods are call and apply, and both can be used to manipulate the value of this in the function on which they are called.

function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);

This is a typical example of using call. It basically takes the first parameter and sets this in the function foo as a reference to thisArg. All other parameters passed to call are passed to the function foo as arguments.
So the above code will log {myObj: "is cool"}, [1, 2, 3] in the console. Pretty nice way to change the value of this in any function.

apply is almost the same as call accept that it takes only two parameters: thisArg and an array which contains the arguments to be passed to the function. So the above call call can be translated to apply like this:

foo.apply(thisArg, [1,2,3])

Note that call and apply can override the value of this set by dot method invocation we discussed in the second bullet. Simple enough :)

Presenting.... bind!

bind is a brother of call and apply. It is also a method inherited by all functions from the global Function constructor in Javascript. The difference between bind and call/apply is that both call and apply will actually invoke the function. bind, on the other hand, returns a new function with the thisArg and arguments pre-set. Let's take an example to better understand this:

function foo (a, b) {
    console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs `function`
console.log (bound);
/* logs `function () { native code }` */

bound(); // calling the function returned by `.bind`
// logs `{myObj: "even more cool now"}, [1, 2]`

See the difference between the three? It is subtle, but they are used differently. Like call and apply, bind will also over-ride the value of this set by dot-method invocation.

Also note that neither of these three functions do any change to the original function. call and apply would return the value from freshly constructed functions while bind will return the freshly constructed function itself, ready to be called.

Extra stuff, copy this

Sometimes, you don't like the fact that this changes with scope, specially nested scope. Take a look at the following example.

var myObj = {
    hello: function () {
        return "world"
        },
    myMethod: function () {
        // copy this, variable names are case-sensitive
        var that = this;
        // callbacks ftw o/
        foo.bar("args", function () {
            // I want to call `hello` here
            this.hello(); // error
            // but `this` references to `foo` damn!
            // oh wait we have a backup o/
            that.hello(); // "world"
        });
    }
  };

In the above code, we see that the value of this changed with nested scope, but we wanted the value of this from the original scope. So we 'copied' this to that and used the copy instead of this. Clever, eh?

Index:

  1. What is held in this by default?
  2. What if we call the function as a method with Object-dot notation?
  3. What if we use the new keyword?
  4. How do we manipulate this with call and apply?
  5. Using bind.
  6. Copying this to solve nested-scope issues.

回答4:

"this" is all about scope. Every function has its own scope, and since everything in JS is an object, even a function can store some values into itself using "this". OOP 101 teaches that "this" is only applicable to instances of an object. Therefore, every-time a function executes, a new "instance" of that function has a new meaning of "this".

Most people get confused when they try to use "this" inside of anonymous closure functions like:

(function(value) {
    this.value = value;
    $('.some-elements').each(function(elt){
        elt.innerHTML = this.value;        // uh oh!! possibly undefined
    });
})(2);

So here, inside each(), "this" doesn't hold the "value" that you expect it to (from

this.value = value;
above it). So, to get over this (no pun intended) problem, a developer could:

(function(value) {
    var self = this;            // small change
    self.value = value;
    $('.some-elements').each(function(elt){
        elt.innerHTML = self.value;        // phew!! == 2 
    });
})(2);

Try it out; you'll begin to like this pattern of programming


回答5:

this in Javascript always refers to the 'owner' of the function that is being executed.

If no explicit owner is defined, then the top most owner, the window object, is referenced.

So if I did

function someKindOfFunction() {
   this.style = 'foo';
}

element.onclick = someKindOfFunction;

this would refer to the element object. But be careful, a lot of people make this mistake

<element onclick="someKindOfFunction()">

In the latter case, you merely reference the function, not hand it over to the element. Therefor, this will refer to the window object.


回答6:

Since this thread has bumped up, I have compiled few points for readers new to this topic.

How is the value of this determined?

We use this similar to the way we use pronouns in natural languages like English: “John is running fast because he is trying to catch the train.” Instead we could have written “… John is trying to catch the train”.

var person = {    
    firstName: "Penelope",
    lastName: "Barrymore",
    fullName: function () {

    // We use "this" just as in the sentence above:
       console.log(this.firstName + " " + this.lastName);

    // We could have also written:
       console.log(person.firstName + " " + person.lastName);
    }
}

this is not assigned a value until an object invokes the function where it is defined. In the global scope, all global variables and functions are defined on the window object. Therefore, this in a global function refers to (and has the value of) the global window object.

When use strict, this in global and in anonymous functions that are not bound to any object holds a value of undefined.

The this keyword is most misunderstood when: 1) we borrow a method that uses this, 2) we assign a method that uses this to a variable, 3) a function that uses this is passed as a callback function, and 4) this is used inside a closure — an inner function. (2)

table

What holds the future

Defined in ECMA Script 6, arrow-functions adopt the this binding from the enclosing (function or global) scope.

function foo() {
     // return an arrow function
     return (a) => {
     // `this` here is lexically inherited from `foo()`
     console.log(this.a);
  };
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };

var bar = foo.call(obj1);
bar.call( obj2 ); // 2, not 3!

While arrow-functions provide an alternative to using bind(), it’s important to note that they essentially are disabling the traditional this mechanism in favor of more widely understood lexical scoping. (1)


References:

  1. this & Object Prototypes, by Kyle Simpson. © 2014 Getify Solutions.
  2. javascriptissexy.com - http://goo.gl/pvl0GX
  3. Angus Croll - http://goo.gl/Z2RacU

回答7:

Every function execution context in javascript has a scope context this parameter that is set by:

  1. How the function is called (including as an object method, use of call and apply, use of new)
  2. Use of bind
  3. Lexically for arrow functions (they adopt the this of their outer execution context)

Whatever that scope context is, is referenced by "this".

You can change that set the value of this scope context using func.call, func.apply or func.bind.

By default, and what confuses most beginners, when a callback listener is called after an event is raised on a DOM element, the scope context this value of the function is the DOM element.

jQuery makes this trivial to change with jQuery.proxy.


回答8:

Here is one good source of this in JavaScript.

Here is the summary:

  • global this

    In a browser, at the global scope, this is the windowobject

    <script type="text/javascript">
      console.log(this === window); // true
      var foo = "bar";
      console.log(this.foo); // "bar"
      console.log(window.foo); // "bar"
    

    In node using the repl, this is the top namespace. You can refer to it as global.

    >this
      { ArrayBuffer: [Function: ArrayBuffer],
        Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
        Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
        ...
    >global === this
     true
    

    In node executing from a script, this at the global scope starts as an empty object. It is not the same as global

    \test.js
    console.log(this);  \ {}
    console.log(this === global); \ fasle
    
  • function this

Except in the case of DOM event handlers or when a thisArg is provided (see further down), both in node and in a browser using this in a function that is not called with new references the global scope…

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();
    console.log(this.foo); //logs "foo"
</script>

If you use use strict;, in which case this will be undefined

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      "use strict";
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();  //Uncaught TypeError: Cannot set property 'foo' of undefined 
</script>

If you call a function with new the this will be a new context, it will not reference the global this.

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    new testThis();
    console.log(this.foo); //logs "bar"

    console.log(new testThis().foo); //logs "foo"
</script>
  • prototype this

Functions you create become function objects. They automatically get a special prototype property, which is something you can assign values to. When you create an instance by calling your function with new you get access to the values you assigned to the prototype property. You access those values using this.

function Thing() {
  console.log(this.foo);
}

Thing.prototype.foo = "bar";

var thing = new Thing(); //logs "bar"
console.log(thing.foo);  //logs "bar"

It is usually a mistake to assign arrays or objects on the prototype. If you want instances to each have their own arrays, create them in the function, not the prototype.

function Thing() {
    this.things = [];
}

var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []
  • object this

You can use this in any function on an object to refer to other properties on that object. This is not the same as an instance created with new.

var obj = {
    foo: "bar",
    logFoo: function () {
        console.log(this.foo);
    }
};

obj.logFoo(); //logs "bar"
  • DOM event this

In an HTML DOM event handler, this is always a reference to the DOM element the event was attached to

function Listener() {
    document.getElementById("foo").addEventListener("click",
       this.handleClick);
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs "<div id="foo"></div>"
}

var listener = new Listener();
document.getElementById("foo").click();

Unless you bind the context

function Listener() {
    document.getElementById("foo").addEventListener("click", 
        this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
    console.log(this); //logs Listener {handleClick: function}
}

var listener = new Listener();
document.getElementById("foo").click();
  • HTML this

Inside HTML attributes in which you can put JavaScript, this is a reference to the element.

<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
  • eval this

You can use eval to access this.

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    eval("console.log(this.foo)"); //logs "bar"
}

var thing = new Thing();
thing.logFoo();
  • with this

You can use with to add this to the current scope to read and write to values on this without referring to this explicitly.

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    with (this) {
        console.log(foo);
        foo = "foo";
    }
}

var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"
  • jQuery this

the jQuery will in many places have this refer to a DOM element.

<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
    this.click();
});
</script>

回答9:

Daniel, awesome explanation! A couple of words on this and good list of this execution context pointer in case of event handlers.

In two words, this in JavaScript points the object from whom (or from whose execution context) the current function was run and it's always read-only, you can't set it anyway (such an attempt will end up with 'Invalid left-hand side in assignment' message.

For event handlers: inline event handlers, such as <element onclick="foo">, override any other handlers attached earlier and before, so be careful and it's better to stay off of inline event delegation at all. And thanks to Zara Alaverdyan who inspired me to this list of examples through a dissenting debate :)

  • el.onclick = foo; // in the foo - obj
  • el.onclick = function () {this.style.color = '#fff';} // obj
  • el.onclick = function() {doSomething();} // In the doSomething - Window
  • el.addEventListener('click',foo,false) // in the foo - obj
  • el.attachEvent('onclick, function () { // this }') // window, all the compliance to IE :)
  • <button onclick="this.style.color = '#fff';"> // obj
  • <button onclick="foo"> // In the foo - window, but you can <button onclick="foo(this)">

回答10:

There is a lot of confusion regarding how "this" keyword is interpreted in JavaScript. Hopefully this article will lay all those to rest once and for all. And a lot more. Please read the entire article carefully. Be forewarned that this article is long.

Irrespective of the context in which it is used, "this" always references the "current object" in Javascript. However, what the "current object" is differs according to context. The context may be exactly 1 of the 6 following:

  1. Global (i.e. Outside all functions)
  2. Inside Direct "Non Bound Function" Call (i.e. a function that has not been bound by calling functionName.bind)
  3. Inside Indirect "Non Bound Function" Call through functionName.call and functionName.apply
  4. Inside "Bound Function" Call (i.e. a function that has been bound by calling functionName.bind)
  5. While Object Creation through "new"
  6. Inside Inline DOM event handler

The following describes each of this contexts one by one:

  1. Global Context (i.e. Outside all functions):

    Outside all functions (i.e. in global context) the "current object" (and hence the value of "this") is always the "window" object for browsers.

  2. Inside Direct "Non Bound Function" Call:

    Inside a Direct "Non Bound Function" Call, the object that invoked the function call becomes the "current object" (and hence the value of "this"). If a function is called without a explicit current object, the current object is either the "window" object (For Non Strict Mode) or undefined (For Strict Mode) . Any function (or variable) defined in Global Context automatically becomes a property of the "window" object.For e.g Suppose function is defined in Global Context as

    function UserDefinedFunction(){
        alert(this)
        }
    

    it becomes the property of the window object, as if you have defined it as

    window.UserDefinedFunction=function(){
      alert(this)
    }  
    

    In "Non Strict Mode", Calling/Invoking this function directly through "UserDefinedFunction()" will automatically call/invoke it as "window.UserDefinedFunction()" making "window" as the "current object" (and hence the value of "this") within "UserDefinedFunction".Invoking this function in "Non Strict Mode" will result in the following

    UserDefinedFunction() // displays [object Window]  as it automatically gets invoked as window.UserDefinedFunction()
    

    In "Strict Mode", Calling/Invoking the function directly through "UserDefinedFunction()" will "NOT" automatically call/invoke it as "window.UserDefinedFunction()".Hence the "current object" (and the value of "this") within "UserDefinedFunction" shall be undefined. Invoking this function in "Strict Mode" will result in the following

    UserDefinedFunction() // displays undefined
    

    However, invoking it explicitly using window object shall result in the following

    window.UserDefinedFunction() // "always displays [object Window]   irrespective of mode."
    

    Let us look at another example. Please look at the following code

     function UserDefinedFunction()
        {
            alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
        }
    
    var o1={
                a:1,
                b:2,
                f:UserDefinedFunction
          }
    var o2={
                c:3,
                d:4,
                f:UserDefinedFunction
           }
    
    o1.f() // Shall display 1,2,undefined,undefined
    o2.f() // Shall display undefined,undefined,3,4
    

    In the above example we see that when "UserDefinedFunction" was invoked through o1, "this" takes value of o1 and the value of its properties "a" and "b" get displayed. The value of "c" and "d" were shown as undefined as o1 does not define these properties

    Similarly when "UserDefinedFunction" was invoked through o2, "this" takes value of o2 and the value of its properties "c" and "d" get displayed.The value of "a" and "b" were shown as undefined as o2 does not define these properties.

  3. Inside Indirect "Non Bound Function" Call through functionName.call and functionName.apply:

    When a "Non Bound Function" is called through functionName.call or functionName.apply, the "current object" (and hence the value of "this") is set to the value of "this" parameter (first parameter) passed to call/apply. The following code demonstrates the same.

    function UserDefinedFunction()
    {
        alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
    }
    var o1={
                a:1,
                b:2,
                f:UserDefinedFunction
           }
    var o2={
                c:3,
                d:4,
                f:UserDefinedFunction
           }
    
    UserDefinedFunction.call(o1) // Shall display 1,2,undefined,undefined
    UserDefinedFunction.apply(o1) // Shall display 1,2,undefined,undefined
    
    UserDefinedFunction.call(o2) // Shall display undefined,undefined,3,4
    UserDefinedFunction.apply(o2) // Shall display undefined,undefined,3,4
    
    o1.f.call(o2) // Shall display undefined,undefined,3,4
    o1.f.apply(o2) // Shall display undefined,undefined,3,4
    
    o2.f.call(o1) // Shall display 1,2,undefined,undefined
    o2.f.apply(o1) // Shall display 1,2,undefined,undefined
    

    The above code clearly shows that the "this" value for any "NON Bound Function" can be altered through call/apply. Also,if the "this" parameter is not explicitly passed to call/apply, "current object" (and hence the value of "this") is set to "window" in Non strict mode and "undefined" in strict mode.

  4. Inside "Bound Function" Call (i.e. a function that has been bound by calling functionName.bind):

    A bound function is a function whose "this" value has been fixed. The following code demonstrated how "this" works in case of bound function

    function UserDefinedFunction()
    {
        alert(this.a + ","  + this.b + ","  + this.c  + ","  + this.d)
    }
    var o1={
              a:1,
              b:2,
              f:UserDefinedFunction,
              bf:null
           }
    var o2={
               c:3,
               d:4,
               f:UserDefinedFunction,
               bf:null
            }
    
    var bound1=UserDefinedFunction.bind(o1); // permanantly fixes "this" value of function "bound1" to Object o1
    bound1() // Shall display 1,2,undefined,undefined
    
    var bound2=UserDefinedFunction.bind(o2); // permanantly fixes "this" value of function "bound2" to Object o2
    bound2() // Shall display undefined,undefined,3,4
    
    var bound3=o1.f.bind(o2); // permanantly fixes "this" value of function "bound3" to Object o2
    bound3() // Shall display undefined,undefined,3,4
    
    var bound4=o2.f.bind(o1); // permanantly fixes "this" value of function "bound4" to Object o1
    bound4() // Shall display 1,2,undefined,undefined
    
    o1.bf=UserDefinedFunction.bind(o2) // permanantly fixes "this" value of function "o1.bf" to Object o2
    o1.bf() // Shall display undefined,undefined,3,4
    
    o2.bf=UserDefinedFunction.bind(o1) // permanantly fixes "this" value of function "o2.bf" to Object o1
    o2.bf() // Shall display 1,2,undefined,undefined
    
    bound1.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
    
    bound1.apply(o2) // Shall still display 1,2,undefined,undefined. "apply" cannot alter the value of "this" for bound function
    
    o2.bf.call(o2) // Shall still display 1,2,undefined,undefined. "call" cannot alter the value of "this" for bound function
    o2.bf.apply(o2) // Shall still display 1,2,undefined,undefined."apply" cannot alter the value of "this" for bound function
    

    As given in the code above, "this" value for any "Bound Function" CANNOT be altered through call/apply. Also, if the "this" parameter is not explicitly passed to bind, "current object" (and hence the value of "this" ) is set to "window" in Non strict mode and "undefined" in strict mode. One more thing. Binding an already bound function does not change the value of "this". It remains set as the value set by first bind function.

  5. While Object Creation through "new":

    Inside a constructor function, the "current object" (and hence the value of "this") references the object that is currently being created through "new" irrespective of the bind status of the function. However if the constructor is a bound function it shall get called with predefined set of arguments as set for the bound function.

  6. Inside Inline DOM event handler:

    Please look at the following HTML Snippet

    <button onclick='this.style.color=white'>Hello World</button>
    <div style='width:100px;height:100px;' onclick='OnDivClick(event,this)'>Hello World</div>
    

    The "this" in above examples refer to "button" element and the "div" element respectively.

    In the first example, the font color of the button shall be set to white when it is clicked.

    In the second example when the "div" element is clicked it shall call the OnDivClick function with its second parameter referencing the clicked div element. However the value of "this" within OnDivClick SHALL NOT reference the clicked div element. It shall be set as the "window object" or "undefined" in Non strict and Strict Modes respectively (if OnDivClick is an unbound function) or set to a predefined Bound value (if OnDivClick is a bound function)

The following summarizes the entire article

  1. In Global Context "this" always refers to the "window" object

  2. Whenever a function is invoked, it is invoked in context of an object ("current object"). If the current object is not explicitly provided, the current object is the "window object" in NON Strict Mode and "undefined" in Strict Mode by default.

  3. The value of "this" within a Non Bound function is the reference to object in context of which the function is invoked ("current object")

  4. The value of "this" within a Non Bound function can be overriden by call and apply methods of the function.

  5. The value of "this" is fixed for a Bound function and cannot be overriden by call and apply methods of the function.

  6. Binding and already bound function does not change the value of "this". It remains set as the value set by first bind function.

  7. The value of "this" within a constructor is the object that is being created and initialized

  8. The value of "this" within an inline DOM event handler is reference to the element for which the event handler is given.


回答11:

Probably the most detailed and comprehensive article on this is the following:

Gentle explanation of 'this' keyword in JavaScript

The idea behind this is to understand that the function invocation types have the significant importance on setting this value.


When having troubles identifying this, do not ask yourself:

Where is this taken from?

but do ask yourself:

How is the function invoked?

For an arrow function (special case of context transparency) ask yourself:

What value has this where the arrow function is defined?

This mindset is correct when dealing with this and will save you from headache.


回答12:

This is the best explanation I've seen. Understand JavaScripts this with Clarity

The this reference ALWAYS refers to (and holds the value of) an object—a singular object—and it is usually used inside a function or a method, although it can be used outside a function in the global scope. Note that when we use strict mode, this holds the value of undefined in global functions and in anonymous functions that are not bound to any object.

There are Four Conditions where this can be

  • 发表于 2019-02-27 08:15
  • 阅读 ( 192 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除