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).
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.
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
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.
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.
In an indirect evaluation, it will be based on the [[GlobalEnv]] attribute (the global environment record of the realm record that performs the indirect evaluation). 李>
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.
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.
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.
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.
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 MemberExpressionrefObj.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`.
};
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.
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.
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.
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
.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.
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:
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 }`.
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.
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`.
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.
if(true){
console.log(this); // What is `this` here?
}
globalThis. The marked line is evaluated within the initial global execution context.
const obj = {};
function myFun(){
return { // What is `this` here?
"is obj": this === obj,
"is globalThis": this === globalThis
};
}
obj.method = myFun;
console.log(obj.method());
The
this
keyword behaves differently in JavaScript compared to other languages. In object-oriented languages, thethis
keyword refers to the current instance of the class. In JavaScript, the value ofthis
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)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.The above
f1
is a method of a global object. So we can also call it on thewindow
object like this: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.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.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.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).
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
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
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. to specify them.
operator.
this
but first created the function instead of writing the object directly.
4. When used inside a constructor.
When a function is used as a constructor (that is, when called using thenew
keyword),
thisin the function body points to the new object being constructed.
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.
6. Internal call(), apply() and bind() functions
Function.prototype
.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])
Setobj1
to the value withinthis
code>fun() and callsfun()
, passing the elements ofargsArray
as its arguments.fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- Set obj1 asfun ()
the value ofthis
and callsfun()
passingarg1, arg2, arg3, ... as its arguments.
fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- Returns a reference to the functionfun
, wherethis
in fun is bound toobj1
, and the parameters offun
are bound to the specified parametersarg1, arg2, arg3, ...
.apply
,call
andbind
must be obvious.apply
Allows arguments to be specified as array-like objects, that is, objects with a numericlength
property and a corresponding non-negative integer property. Andcall
allows the parameters of the function to be specified directly. Bothapply
andcall
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.7.
within the event handlerthis
this
directly within the event handler function to reference the corresponding element. This direct function assignment can be done using theaddeventListener
method or via traditional event registration methods such asonclick
.this
), it refers to the element.
this
through other functions called within event handlers or event properties resolves to the global objectwindow
.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中更好地尝试此操作>.
8.
in ES6 arrow functionsthis
In an arrow function,
this
behaves like a public variable: it will be inherited from its lexical scope. Thethis
of the function that defines the arrow function will be thethis
of the arrow function.So, this is the same behavior as:
Please refer to the following code:
This
is a keyword in JavaScript that is an attribute of the execution context. Its main use is in functions and constructors. The rules forthis
are pretty simple (if you stick to best practices).Technical description of
this
in the specificationECMAScript StandardDefinition
this
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
The return value ofthis
usually depends on whether the code is in strict mode.GetThisBinding reflects the value of
this
for the current execution context, sothis
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 alwaysundefined
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.eval
calls usually look likeeval(
...);
or(eval)(
...); (or((eval))(
…);
, etc.). 1 This is only direct (if the calling expression conforms to the narrow pattern). 2eval
The call involves calling a function referenceeval
in any other way. It can beeval?.(
...)
,(
..., eval)(
...),
, callingwindow.eval(
...)
,eval.call(
...,
...) Given const aliasEval1 = eval; etc. window.aliasEval2 = eval;
, oraliasEval1(
…)
,aliasEval2(
…)
code>.respectively Given const originalEval = eval; window.eval = (x) => originalEval(x);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 thethis
value from.Then, if
this
appears ineval
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:
this
value does not change; it is taken from the lexical scope namedeval
.this
value is the global object (globalThis
).What about new functions
? —new Function
is similar toeval
, 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:
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:
or:
In any of the following function calls, the value of
this
withinfunc
will berefObj
. 1refObj.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, inrefObj.func()
(orrefObj["func"]()
), CallMemberExpression is the entire expressionrefObj.func()
, which consists of MemberExpressionrefObj.func
and Parameters()
.Moreover,
refObj.func
andrefObj
play three roles, respectively:refObj.func
As a value is a callable function object; the corresponding reference is used to determinethis
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 torefObj.func
; orfoo.bar
code> when applied atfoo.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:Without base reference, strict mode and
with
Calls without a base reference are usually functions that are not called as attributes. For example:
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 thatj
can only return the function object (Value) itself, not the reference record. Therefore, the base referencerefObj
is lost.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 caseundefined
. In "sloppy mode", undefined or null thisArgument causesthis
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 controlwith
binding behavior.To summarize, so far:
and:
Please note that when calculating
this
, the position where a normal function is defined does not matter..call
,.apply
,.bind
, thisArg and primitivesOrdinaryCallBindThis 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:
For objects, this is the same in strict and non-strict modes.
Now, try providing a primitive value:
In non-strict mode, primitives are forced to their object wrapper form. It is the same object type you get when calling
Object("s")
ornew String("s")
. In strict mode, you can use primitives:Libraries utilizing these methods, such as jQuery set
this
to the DOM element selected here: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. notclass extends
...{
...}
), it sets thisArgument to A new object created from the constructor's prototype. Properties set onthis
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.Class definitions are implicitly in strict mode:
super
H4> The exception to thenew
behavior isclass 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 ofsuper
calls (without their ownconstructor occurs implicitly in the case of function
). It is not allowed to usethis
before callingsuper
.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 forsuper
calls. It uses BindThisValue to setthis
to that environment record.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:this
refers to the class itself,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.1:
(o.f)()
is equivalent too.f()
;(f)()
is equivalent tof()
. 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 supplyingthis 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,
MDNprovides great service documentation.
Just for fun, test your understanding with some examplesFor each code snippet, answer the following question:
"What is the value of
thisat the marked line? Why?" .
To show the answer, click on the gray box.