Understand this keyword thoroughly

Posted by jibster on Tue, 16 Jun 2020 09:36:53 +0200

Compared with other languages, the performance of this keyword of function in JavaScript is slightly different. In addition, there will be some differences between strict mode and non strict mode.

catalog

1. Global environment

2. Function (in operation) environment

3.call and apply

4. bind method

5. Method as object

6. this of arrow function

7. this on the prototype chain

8. this in getter and setter

9. this in the constructor

10. this of DOM event handler

In most cases, the way the function is called determines the value of this. This cannot be assigned during execution, and may have a different value each time a function is called. ES5 introduces the bind method to set this value of the function, regardless of how the function is called. ES2015 introduces an arrow function that supports this lexical parsing (it sets this value in a closed execution environment).

The environment object of current code execution always points to an object in non strict mode, and can be any value in strict mode

1. Global environment

Whether in strict mode or not, this points to a global object in the global execution environment (outside any function body).

// In the browser, the window object is also a global object:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = "jhj";
console.log(window.b)  // "jhj"
console.log(b)         // "jhj"

2. Function (in operation) environment

Within a function, the value of this depends on how the function is called. (key points)

Because the following code is not in strict mode and the value of this is not set by the call, the value of this points to the global object by default.

function f1(){
  return this;
}
//In the browser:
f1() === window;   //In the browser, the global object is window

//In Node:
f1() === global;   

However, in strict mode, this will keep the value when it enters the execution environment, so the following this will default to undefined.

function f2(){
  "use strict"; // This is the strict model
  return this;
}

f2() === undefined; // true

Therefore, in strict mode, if this is not defined by execution context, it will remain undefined.

3.call and apply

If you want to transfer the value of this from one environment to another, you need to use the call and apply methods.

// this is bound to an object as the first parameter of call and apply.
var obj = {a: 'Custom'};

// This property is defined in the global object.
var a = 'Global';

function whatsThis(arg) {
  return this.a;  // The value of this depends on how the function is called
}

whatsThis();          // 'Global'
whatsThis.call(obj);  // 'Custom'
whatsThis.apply(obj); // 'Custom'

When a function uses this keyword in its body, it can be inherited from the Function.prototype The call and apply methods of bind this value to a specific object in the call.

function add(c, d) {
  return this.a + this.b + c + d;
}

var o = {a: 1, b: 3};

// The first parameter is the object used as' this'
// Subsequent parameters are passed to function calls as parameters
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16

// The first parameter is also the object used as' this'
// The second argument is an array in which the elements are used as arguments in the function call
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

When using the call and apply functions, note that if the value passed to this is not an object, JavaScript attempts to convert it to an object using the internal ToObject operation. Therefore, if the passed value is an original value such as' 7 'or' foo ', it will be converted to an object using the relevant constructor, so the original value of' 7 'will be converted to an object, such as' new Number(7), and the string' foo 'will be converted to a new String('foo'), such as:

function bar() {
  console.log(Object.prototype.toString.call(this));
}

//Original value 7 is implicitly converted to an object
bar.call(7); // [object Number]
bar.call('foo'); // [object String]

4. bind method

Calling f.bind(someObject) will create a function with the same function body and scope as F, but in this new function, this will be permanently bound to the first parameter of bind. No matter how the function is called, it only takes effect once

function f(){
  return this.a;
}

var g = f.bind({a:"jhj"});
console.log(g()); // jhj

var h = g.bind({a:'yoo'}); // bind only works once!
console.log(h()); // jhj

var o = {a:37, f:f, g:g, h:h};
console.log(o.a, o.f(), o.g(), o.h()); // 37, 37, jhj, jhj

5. Method as object

When functions are called as methods in objects, their this is the object that calls the function.

In the following example, when o.f() is called, this within the function is bound to the O object.

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // 37

Note that such behavior is not affected at all by how or where the function is defined. In the previous example, we defined the function inline as member f while defining object o. However, we can also define a function and then attach it to o.f. Doing so results in the same behavior:

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;

console.log(o.f()); // 37

 

6. this of arrow function

This in the arrow function and this set to the environment when he was created in the global code, it will be set as a global object

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

If this is passed to call, bind, or apply to call the arrow function, it is ignored. You can still add parameters to the call, though the first parameter (thisArg) should be set to null.

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
// As a method call of an object
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// Try using call to set this
console.log(foo.call(obj) === globalObject); // true

// Try using bind to set this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

No matter how you call it or how you toss it, it's useless. this will not change. It's only related to the environment when you define it.

In any case, foo's this is set to the environment in which it was created (in the above example, the global object). This also applies to arrow functions created within other functions: these arrow functions have this set to a closed lexical environment.

Look at the example below for some inspiration

// Create an obj object with the bar method,
// bar returns a function,
// This function returns this,
// The returned function is created as an arrow function,
// So its this is permanently bound to this of its outer function.
// The value of bar can be set in the call, which in turn sets the value of the return function.
var obj = {
  bar: function() {
    var x = (() => this);
    return x;
  }
};

// Call bar as a method of obj object and bind its this to obj.
// Assign a reference to the returned function to fn.
var fn = obj.bar();

// Call fn directly without setting this,
// Generally (i.e. without the arrow function), it defaults to a global object
// undefined if in strict mode
console.log(fn() === obj); // true

// But notice that if you just refer to obj's methods,
// Without calling it
var fn2 = obj.bar;
// After calling the arrow function, this points to window because it inherits this from bar.
console.log(fn2()() == window); // true

7. this on the prototype chain

this in the prototype chain

The same concept applies to methods defined somewhere on the object prototype chain. If the method exists on the prototype chain of an object, then this points to the object that calls the method, just as the method is on the object.

var o = {
  f: function() { 
    return this.a + this.b; 
  }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5

In this case, object p does not have its own f attribute, and its f attribute is inherited from its prototype. Although in the process o f finding F, it doesn't matter that the f attribute is found in o at last; the search process starts with the reference of p.f, so this in the function points to p. That is, because f is called as a method of p, its this points to p. This is an interesting feature of prototype inheritance in JavaScript.

8. this in getter and setter

Again, the same concept applies when a function is called in a getter or setter. Functions used as getters or setters bind this to objects that set or get properties.

function sum() {
  return this.a + this.b + this.c;
}

var o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return (this.a + this.b + this.c) / 3;
  }
};

Object.defineProperty(o, 'sum', {
    get: sum, enumerable: true, configurable: true});

console.log(o.average, o.sum); // logs 2, 6

9. this in the constructor

When a function is used as a constructor (using the new keyword), its this is bound to the new object being constructed.

/*
 * The constructor works like this:
 *
 * function MyConstructor(){
 *   // Function entities are written here
 *   // Create properties on this as needed and assign them, such as:
 *   this.fum = "nom";
 *   // wait...
 *
 *   // If a function has a return statement that returns an object,
 *   // The object will be the result of a new expression. 
 *   // Otherwise, the result of the expression is the object currently bound to this.
 *   //(That is, the common situation you usually see).
 * }
 */

function C(){
  this.a = 37;
}

var o = new C();
console.log(o.a); // logs 37


function C2(){
  this.a = 37;
  return {a:38};
}

o = new C2();
console.log(o.a); // logs 38

In the previous example (C2), because the return object was manually set during the call to the constructor, the default object bound to this was discarded. (this basically makes the statement "this.a = 37;" a zombie code, which is not really a "zombie". This statement is executed, but it has no impact on the outside, so it can be ignored completely.).

10. this of DOM event handler

When a function is used as an event handler, its this points to the element that triggers the event (some browsers do not adhere to this Convention when using functions other than addEventListener to dynamically add listener functions).

// When called, changes the associated element to blue
function bluify(e){
  console.log(this === e.currentTarget); // Always true

  // true when currentTarget and target are the same object
  console.log(this === e.target);        
  this.style.backgroundColor = '#A5D9F3';
}

// Get a list of all elements in the document
var elements = document.getElementsByTagName('*');

// Use bluefy as the click monitor function of an element. When an element is clicked, it will turn blue
for(var i=0 ; i<elements.length ; i++){
  elements[i].addEventListener('click', bluify, false);
}

When the code is called by an inline event, its this points to the DOM element where the listener is located:

<button onclick="alert(this.tagName.toLowerCase());">
  Show this
</button>

The alert above shows a button. Note that only this in the outer code is set as follows:

<button onclick="alert((function(){return this})());">
  Show inner this
</button>

In this case, this is not set for the intrinsic function, so it points to the global/window object (that is, the default object that the function called in the non strict mode points to when this is not set).

Article reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

 

 

Topics: Javascript Attribute