Understand the function and appropriate usage of the "this" keyword
P粉155551728
P粉155551728 2023-10-12 15:50:58
0
2
588

I would like to find a clear explanation of what the "this" keyword does and how to use it correctly.

It seems to be behaving strangely and I don't entirely understand why.

thisHow does it work and when should you use it?

P粉155551728
P粉155551728

reply all(2)
P粉087951442

The this keyword behaves differently in JavaScript compared to other languages. In object-oriented languages, the this keyword refers to the current instance of the class. In JavaScript, the value of this is determined by the function's calling context (context.function()) and the location from which it was called. p>

1. When used in a global context

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

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

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

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

The above f1 is a method of a global object. So we can also call it on the window object like this:

function f()
{
    return this;
}

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

2. When used inside an object method

When you use the this keyword in an object method, this is bound to the "direct" 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 "immediately" in double quotes. This is to indicate that if an object is nested within another object, this is bound to the immediate parent object.

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

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

Even if you explicitly add function as a method to an object, it still follows the above rules, i.e. this still points to the direct 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 calling a context-free function

When you use this inside a function that is called without any context (i.e. not on any object), it is bound to the global object (window# in the 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

Try everything with functions

We can also use functions to try the above points. But there are still some differences.

    Above we added members to the object using object literal notation. We can add members to a function using
  • this. to specify them.
  • Object literal notation creates an object instance that we can use immediately. For functions, we may need to first create an instance of it using the
  • new operator.
  • Also in the object literal method, we can use the dot operator to explicitly add members to the defined object. This is only added to specific instances. However, I have added the variable to the function prototype so that it is reflected in all instances of the function.
Below I tried everything I did above with Object and

this but first created the function instead of writing the object directly.

/********************************************************************* 
  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() + "
"); //[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() + "
"); //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() + "
"); //v1 obj2.incrementVersion(); //incrementing version in obj2 //does not affect obj1 version document.write(obj2.getVersion() + "
"); //v2 document.write(obj1.getVersion() + "
"); //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() + "
"); //[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()+"
");//999 //calling obj3.fun() makes //ProtoObj.fun() to access obj3.a as //if fun() is defined on obj3

4. When used inside a constructor.

When a function is used as a constructor (that is, when called using the

new keyword), this in the 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 a function defined on the prototype chain

If the method is on the object's prototype chain,

this within the method refers to the object on which the method is called, as if the method was defined on that 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. Internal call(), apply() and bind() functions

  • All these methods are defined on Function.prototype.
  • These methods allow writing a function once and calling it in different contexts. In other words, they allow specifying the this value that will be used when executing the function. They can also pass any arguments to the original function when it is called.
  • fun.apply(obj1 [, argsArray]) Set obj1 to the value within this code>fun() and calls fun(), passing the elements of argsArray as its arguments.
  • fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - Set obj1 as fun () the value of this and calls fun() passing arg1, arg2, arg3, ... as its arguments.
  • fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]]) - Returns a reference to the functionfun, where this in fun is bound to obj1, and the parameters of fun are bound to the specified parameters arg1, arg2, arg3, ....
  • Now, the difference between apply, call and bind must be obvious. apply Allows arguments to be specified as array-like objects, that is, objects with a numeric length property and a corresponding non-negative integer property. And call allows the parameters of the function to be specified directly. Both apply and call immediately call the function in the specified context and with the specified parameters. bind, on the other hand, simply returns the function bound to the specified this value and arguments. We can capture a reference to this returned function by assigning it to a variable, which we can then call at any time.
function add(inc1, inc2)
{
    return this.a + inc1 + inc2;
}

var o = { a : 4 };
document.write(add.call(o, 5, 6)+"
"); //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]) + "
"); //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()+"
"); //15 var h = add.bind(o, 5); //h: `o.a` i.e. 4 + 5 + ? document.write(h(6) + "
"); //15 // 4 + 5 + 6 = 15 document.write(h() + "
"); //NaN //no parameter is passed to h() //thus inc2 inside add() is `undefined` //4 + 5 + undefined = NaN

7. this

within the event handler
  • When you assign a function directly to an element's event handler, use this directly within the event handler function to reference the corresponding element. This direct function assignment can be done using the addeventListener method or via traditional event registration methods such as onclick.
  • Similarly, when you use this), it refers to the element.
  • However, indirect use of this through other functions called within event handlers or event properties resolves to the global object window.
  • The same behavior as above can be achieved when we attach a function to an event handler using Microsoft's event registration model method attachEvent. Instead of assigning a function to an event handler (thus creating a function method of the element), it calls the function on the event (effectively calling it in the global context).

I suggest to use JSFiddle中更好地尝试此操作>.

sssccc

Using `this` "directly" inside event handler or event property



Using `this` "indirectly" inside event handler or event property



IE only:

8. this

in ES6 arrow functions

In an arrow function, this behaves like a public variable: it will be inherited from its lexical scope. The this of the function that defines the arrow function will be the this of the arrow function.

So, this is the same behavior as:

(function(){}).bind(this)

Please refer to the following code:

const globalArrowFunction = () => {
  return this;
};

console.log(globalArrowFunction()); //window

const contextObject = {
  method1: () => {return this},
  method2: function(){
    return () => {return this};
  }
};

console.log(contextObject.method1()); //window

const contextLessFunction = contextObject.method1;

console.log(contextLessFunction()); //window

console.log(contextObject.method2()()) //contextObject

const innerArrowFunction = contextObject.method2();

console.log(innerArrowFunction()); //contextObject 
P粉156532706

This is a keyword in JavaScript that is an attribute of the execution context. Its main use is in functions and constructors. The rules for this are pretty simple (if you stick to best practices).

Technical description of this in the specification

ECMAScript StandardDefinitionthis By abstract operation (abbreviated as AO) ResolveThisBinding:

Global environment records, Module environment records, and Function environment recordseach have their own GetThisBinding method.

GetThisEnvironment AO finds the LexicalEnvironment of the current running execution context and finds the closest ascending environment record (by iterating through its [[OuterEnv]] property) that has this Binding (i.e. HasThisBinding returns true). The process ends with one of three environment record types.

The value of

this usually depends on whether the code is in strict mode.

The return value of

GetThisBinding reflects the value of this for the current execution context, so this will resolve to a different value each time a new execution context is established. This can also happen when the current execution context is modified. The following subsections list five scenarios in which this might occur.

You can place code samples into the AST Explorer to follow the specification details.

1. Global execution context in scripts

This is the script code evaluated at the top level, e.g. directly in 内:


When within the script's initial global execution context, evaluating this causes GetThisBinding to take the following steps:

The global environment record's [[GlobalThisValue]] property is always set to a host-defined global object accessible via globalThis ( on the Web window, global on Node.js; Documentation on MDN). Follow the steps in InitializeHostDefinedRealm to learn how the [[GlobalThisValue]] property is generated.

2. Global execution context in module

Modules were introduced in ECMAScript 2015.

This applies to modules e.g. directly inside , rather than simply .

When within the module's initial global execution context, evaluating this causes GetThisBinding to take the following steps:

In a module, the value of this is always undefined in the global context. The module is implicitly in strict mode.

3. Enter evalcode

There are two types of eval calls: direct and indirect. This distinction has existed since ECMAScript fifth edition.

  • Direct eval calls usually look like eval(...); or (eval)(...); (or ((eval))();, etc.). 1 This is only direct (if the calling expression conforms to the narrow pattern). 2
  • Indirect evalThe call involves calling a function reference eval in any other way. It can be eval?.(...), (..., eval)(... ), window.eval(...), eval.call(...,...) Given const aliasEval1 = eval; etc. window.aliasEval2 = eval;, or aliasEval1(), aliasEval2()code>.respectively Given const originalEval = eval; window.eval = (x) => originalEval(x);, calling eval() is also indirect.

See chuckj's answer to "(1, eval)('this') vs eval('this') in JavaScript?" and Dmitry Soshnikov's ECMA-262-5 Details – Chapter 2: Strict Mode (Archived) for situations where you may use indirection eval() Call situation.

PerformEval Execute eval code. It creates a new Declarative Environment Record as its LexicalEnvironment, which is what GetThisEnvironment gets the this value from.

Then, if this appears in eval code, GetThisEnvironment is called and its value is returned.

The declarative environment record created depends on whether the eval call was direct or indirect:

this means:

  • In direct evaluation, the this value does not change; it is taken from the lexical scope named eval.
  • In indirect eval, the this value is the global object (globalThis).

What about new functions? — new Function is similar to eval, but it does not call the code immediately; it creates a function. this Binding does not apply anywhere here, except when the function is called, and the function works properly, as described in the next subsection.

4. Enter functioncode

Enter the function code when calling the function.

There are four categories of syntax for calling functions.

The actual function call occurs at the Call AO, which is determined by the context of the call using thisValue; this parameter is passed in a long chain of calls related to the call. Call Internal slot for calling [[Call]] function. This calls PrepareForOrdinaryCall where a new function environment record is created:

In addition, there is also the [[ThisValue]] field in the function environment record:

NewFunctionEnvironment The call also sets the [[ThisBindingStatus]] property of the function environment.

[[Call]] also calls OrdinaryCallBindThis, where the appropriate thisArgument is determined based on:

  • Original reference,
  • The type of function, and
  • Whether the code is in strict mode.

After confirmation, the BindThisValue method of the newly created function environment record is finally called to actually set the [[ThisValue]] field to be added to thisArgument.

Finally, this field is Function environment record GetThisBinding AO gets the value of this from the following location:

Again, the precise determination of this value depends on many factors; this is just a general overview. With this technical background, let's look at all the concrete examples.

Arrow function

When evaluating the arrow function , the [[ThisMode]] internal slot function object's property is set to "lexical" in OrdinaryFunctionCreate. p>

At OrdinaryCallBindThis it takes a function F:

This simply means that the rest of the algorithm binding this is skipped. Arrow functions do not bind their own this value.

So, what is this in the arrow function? Recalling ResolveThisBinding and GetThisEnvironment, the HasThisBinding method explicitly returns false.

Therefore, we iteratively look for the external environment. The process will end in one of three environments with this bindings.

This just means that, in the arrow function body, this comes from the lexical scope of the arrow function, or in other words (from the arrow function vs. function declaration/expression Formula: Are they equivalent/interchangeable?):

FunctionProperties

In ordinary functions (function, method), this is determined by the function calling method.

This is where these "syntax variations" come in handy.

Consider this object containing a function:

const refObj = {
    func: function(){
      console.log(this);
    }
  };

or:

const refObj = {
    func(){
      console.log(this);
    }
  };

In any of the following function calls, the value of this within func will be refObj. 1

  • refObj.func()
  • refObj["func"]()
  • refObj?.func()
  • refObj.func?.()
  • refObj.func``

If the called function is syntactically a property of a base object, then that base object will be the "reference" of the call, which in the normal case will be the value of this. The evaluation steps linked above explain this; for example, in refObj.func() (or refObj["func"]()), CallMemberExpression is the entire expression refObj.func(), which consists of MemberExpression refObj.func and Parameters ().

Moreover, refObj.func and refObj play three roles, respectively:

  • They are all expressions,
  • They are all references, and
  • They are all values.

refObj.func As a value is a callable function object; the corresponding reference is used to determine this binding.

The optional link and tag template examples work very similarly: basically, the reference is ?.() before, `` before, or ()代码>.

EvaluateCall Use IsPropertyReference to determine whether it is syntactically a property of the object. It attempts to obtain the [[Base]] property of the reference (e.g. refObj when applied to refObj.func; or foo.bar code> when applied at foo.bar.baz). If written as a property, GetThisValue will get this [[Base]] property and use it as the this value.

Note: Getters / Setters working methods and methods, regarding this. Simple properties do not affect the execution context, like here, this in the global scope:

const o = {
    a: 1,
    b: this.a, // Is `globalThis.a`.
    [this.a]: 2 // Refers to `globalThis.a`.
  };

Without base reference, strict mode and with

Calls without a base reference are usually functions that are not called as attributes. For example:

func(); // As opposed to `refObj.func();`.

This also occurs when passing or assigning to a method , or using the comma operator . This is where the difference between the reference record and the value is relevant.

Attention to function j: According to the specification, you will notice that j can only return the function object (Value) itself, not the reference record. Therefore, the base reference refObj is lost.

const g = (f) => f(); // No base ref.
const h = refObj.func;
const j = () => refObj.func;

g(refObj.func);
h(); // No base ref.
j()(); // No base ref.
(0, refObj.func)(); // Another common pattern to remove the base ref.

EvaluateCall Call Call , where thisValue is undefined. This is different in OrdinaryCallBindThis (F: function object; thisArgument: thisValue passed to Call) :

Note: Step 5 sets the actual value of this to the thisArgument provided in strict mode - in this case undefined. In "sloppy mode", undefined or null thisArgument causes this to become the global this value.

If IsPropertyReference returns false, then EvaluateCall takes the following steps:

This is undefined thisValue May come from: refEnv. WithBaseObject() is always undefined, except # sec-with-statement-runtime- semantics-evaluation" rel="noreferrer">with statement. In this case, thisValue will be the binding object.

There is also Symbol.unscopables (Documentation on MDN) to control with binding behavior.

To summarize, so far:

function f1(){
  console.log(this);
}

function f2(){
  console.log(this);
}

function f3(){
  console.log(this);
}

const o = {
    f1,
    f2,
    [Symbol.unscopables]: {
      f2: true
    }
  };

f1(); // Logs `globalThis`.

with(o){
  f1(); // Logs `o`.
  f2(); // `f2` is unscopable, so this logs `globalThis`.
  f3(); // `f3` is not on `o`, so this logs `globalThis`.
}

and:

"use strict";

function f(){
  console.log(this);
}

f(); // Logs `undefined`.

// `with` statements are not allowed in strict-mode code.

Please note that when calculating this, the position where a normal function is defined does not matter.

.call, .apply, .bind, thisArg and primitives

OrdinaryCallBindThis Another result of step 5, unlike step 6.2 (in the specification), is that the original this value in "sloppy" mode is only Cast to object.

To check this, let's introduce another source of this values: three methods that override the this binding: 4

  • Function.prototype.apply(thisArg, argArray)
  • Function.prototype. {Call, Bind} (thisArg, ...args)李>

.bind Creates a bound function whose this binding is already set to thisArg and cannot be changed again. .call and .apply call the function immediately and set this to be bound to thisArg.

.call and .apply map directly to Call , using the specified thisArg. .bind Use BoundFunctionCreate to create a bound function. They have their own [[Call ]] method, which looks for the [[BoundThis]] internal slot of the function object.

Example of setting a custom this value:

function f(){
  console.log(this);
}

const myObj = {},
  g = f.bind(myObj),
  h = (m) => m();

// All of these log `myObj`.
g();
f.bind(myObj)();
f.call(myObj);
h(g);

For objects, this is the same in strict and non-strict modes.

Now, try providing a primitive value:

function f(){
  console.log(this);
}

const myString = "s",
  g = f.bind(myString);

g();              // Logs `String { "s" }`.
f.call(myString); // Logs `String { "s" }`.

In non-strict mode, primitives are forced to their object wrapper form. It is the same object type you get when calling Object("s") or new String("s"). In strict mode, you can use primitives:

"use strict";

function f(){
  console.log(this);
}

const myString = "s",
  g = f.bind(myString);

g();              // Logs `"s"`.
f.call(myString); // Logs `"s"`.

Libraries utilizing these methods, such as jQuery set this to the DOM element selected here:

$("button").click(function(){
  console.log(this); // Logs the clicked button.
});

Constructor, Class and New

When a function is called as a constructor using the new operator, EvaluateNew calls Construct, which calls the [[Construct]] method . If the function is a base constructor (i.e. not class extends...{...}), it sets thisArgument to A new object created from the constructor's prototype. Properties set on this in the constructor will eventually appear on the generated instance object. this is returned implicitly unless you explicitly return your own non-primitive value.

class is a new way of creating constructors, introduced in ECMAScript 2015.

function Old(a){
  this.p = a;
}

const o = new Old(1);

console.log(o);  // Logs `Old { p: 1 }`.

class New{
  constructor(a){
    this.p = a;
  }
}

const n = new New(1);

console.log(n); // Logs `New { p: 1 }`.

Class definitions are implicitly in strict mode:

class A{
  m1(){
    return this;
  }
  m2(){
    const m1 = this.m1;
    
    console.log(m1());
  }
}

new A().m2(); // Logs `undefined`.

super H4> The exception to the

new behavior is class extends...{...}, as described above. Derived classes do not set their this value immediately when called; they do so only after reaching the base class through a series of super calls (without their own constructor occurs implicitly in the case of function ). It is not allowed to use this before calling super.

Call super Calls the super constructor with the this value from the lexical scope (function environment record) of the call. GetThisValue There is a special rule for super calls. It uses BindThisValue to set this to that environment record.

class DerivedNew extends New{
  constructor(a, a2){
    // Using `this` before `super` results in a ReferenceError.
    super(a);
    this.p2 = a2;
  }
}

const n2 = new DerivedNew(1, 2);

console.log(n2); // Logs `DerivedNew { p: 1, p2: 2 }`.

5. Evaluation class fields

Instance fields and static fields were introduced in ECMAScript 2022.

When evaluating class, ClassDefinitionEvaluation is executed, modifying the running execution context. For each ClassElement:

  • If the field is static, this refers to the class itself,
  • If the field is not static, this refers to the instance.

Private fields (e.g. #x) and methods are added to the private environment.

Static Blocks is currently a TC39 Phase 3 proposal. Static blocks work the same way as static fields and methods: this within them refers to the class itself.

Note that in methods and getters/setters, this works the same as in normal function properties.

class Demo{
  a = this;
  b(){
    return this;
  }
  static c = this;
  static d(){
    return this;
  }
  // Getters, setters, private modifiers are also possible.
}

const demo = new Demo;

console.log(demo.a, demo.b()); // Both log `demo`.
console.log(Demo.c, Demo.d()); // Both log `Demo`.

1: (o.f)() is equivalent to o.f(); (f)() is equivalent to f(). This 2ality article (Archived). See especially How to evaluate ParenthesizedExpression.

2: It must be a MemberExpression em>, cannot be a property, must have the exact "eval" of [[ReferencedName]] , and must be a %eval% intrinsic object.

3: Whenever the specification says "let ref be the result of evaluating some expression. For example, evaluating MemberExpression or CallExpression is these algorithms. Some of these generate reference records. 4

: There are several other native and host methods that allow supplying

this values, notably Array.prototype.map, Array .prototype.forEach etc. accept thisArg as the second argument. Anyone can create their own method to change this, for example (func, thisArg) => func.bind(thisArg), (func, thisArg) => func . call(thisArg) etc. As always, MDN provides great service documentation. Just for fun, test your understanding with some examples


For each code snippet, answer the following question:

"What is the value of

this

at the marked line? Why?" . To show the answer, click on the gray box.

  1. if(true){
      console.log(this); // What is `this` here?
    }
    

    globalThis. The marked line is evaluated within the initial global execution context.

  2. const obj = {};
    
    function myFun(){
      return { // What is `this` here?
        "is obj": this === obj,
        "is globalThis": this === globalThis
      };
    }
    
    obj.method = myFun;
    
    console.log(obj.method());
    
       
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template