2

8 Different Places “this” Can Point to in JavaScript

 2 years ago
source link: https://blog.bitsrc.io/where-exactly-does-this-point-to-in-javascript-2be1e3fad1fd
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

8 Different Places “this” Can Point to in JavaScript

To know what ‘this’ points to, you have to know how the related function is called in JavaScript

The function’s this is bound when called, depending entirely on where the function is called (that is, how the function is called). To know what this points to, you have to know how the related function is called.

1. Global context

In both non-strict and strict modes this refers to the top-level object (window in the browser).

this === window // true
'use strict'
this === window;
this.name = 'dog';
console.log(this.name); // dog

2. Function context

var name = 'window';
var doSth = function(){
console.log(this.name);
}
doSth(); // 'window'

You might mistakenly think window.doSth() is called and therefore points to window. Although in this case window.doSth does equal doSth. Name equals window.name. This is because in ES5, global variables are mounted in the top-level object (browser is Window).

In fact, it’s not.

let name2 = 'window2';
let doSth2 = function(){
console.log(this === window);
console.log(this.name2);
}
doSth2() // true, undefined

In this example, let does not add attributes to the top-level object (the browser is Window); window.name2 and window.doSth are both undefined.

In strict mode, this in normal functions behaves differently, as undefined.

'use strict'
var name = 'window';
var doSth = function(){
console.log(typeof this === 'undefined');
console.log(this.name);
}
doSth(); // true,// Cannot read properties of undefined (reading 'name')

This is called default binding. Readers familiar with call and apply will use the analogy:

doSth.call(undefined);
doSth.apply(undefined);

The effect is the same. One of the things that call apply does is to change what this refers to as the first parameter in a function. The first argument is undefined or null, which in non-strict mode points to window. In strict mode, it refers to the first parameter. The following article explains in detail.

There is often code like this (the callback function), which is actually a normal function call pattern.

var name = 'dog';
setTimeout(function(){
console.log(this.name);
}, 0);

setTimeout(fn | code, 0, arg1, arg2, ...)

fn.call(undefined, arg1, arg2, ...);

3. Object function (method) invocation pattern

However, there are often situations where a function in an object is assigned to a variable. This is actually a normal function again, so use the rules of normal functions (default binding).

var studentDoSth = student.doSth;
studentDoSth(); // 'window'
// call like this :
studentDoSth.call(undefined);

4. call(), apply(), bind()

Above mentioned call, apply, here is a detailed interpretation. Function.prototype.call(), Check out my other article on how they are implemented

fun.call(thisArg, arg1, arg2, ...)

According to the description of the parameter thisArg, it can be known that call is to change the this in the function to point to thisArg and execute the function, which makes JS much more flexible. In strict mode, thisArg is a primitive value that is a value type, that is, a primitive value. will not be wrapped into an object. for example:

var doSth = function(name){
console.log(this);
console.log(name);
}
doSth.call(2, 'dog'); // Number{2}, 'dog'
var doSth2 = function(name){
'use strict';
console.log(this);
console.log(name);
}
doSth2.call(2, 'dog'); // 2, 'dog'

5. Constructor call pattern

function Student(name){
this.name = name;
console.log(this); // {name: 'dog'}

// return this;
}
var result = new Student('dog');

From this, we can know that when the new operator is called, this points to the new object generated.

6. Calling Patterns in the Prototype Chain

function Student(name){
this.name = name;
}
var s1 = new Student('dog');
Student.prototype.doSth = function(){
console.log(this.name);
}
s1.doSth(); // 'dog'

Will find this familiar. This is the method invocation pattern on an object. Naturally, it points to the new object generated. If the object inherits from other objects. It will also look up through the prototype chain.

7. Arrow function call pattern

Let’s first look at the important differences between arrow functions and ordinary functions:

  • It does not have its own this, super, arguments and new.target bindings.
  • You cannot use new to call.
  • There is no prototype object.
  • The binding of this cannot be changed.
  • The formal parameter name cannot be repeated.

There is no this binding in an arrow function, its value must be determined by looking up the scope chain. If the arrow function is contained by a non-arrow function, this is bound to the this of the nearest non-arrow function, otherwise the value of this is set to the global object. for example:

var name = 'window';
var student = {
name: 'dog',
doSth: function(){
// var self = this;
var arrowDoSth = () => {
// console.log(self.name);
console.log(this.name);
}
arrowDoSth();
},
arrowDoSth2: () => {
console.log(this.name);
}
}
student.doSth(); // 'dog'
student.arrowDoSth2(); // 'window'

In fact, it is equivalent to this outside the arrow function, which is the cached this of the ordinary function above the arrow function. If there is no normal function, it is the global object (window in browsers).

That is to say, it is impossible to bind the this of the arrow function through call, apply, and bind (it itself does not have this). And call, apply, bind can bind the this of the ordinary function above the cache arrow function.
for example:

var student = {
name: 'dog',
doSth: function(){
console.log(this.name);
return () => {
console.log('arrowFn:', this.name);
}
}
}
var person = {
name: 'person',
}
student.doSth().call(person); // 'dog' 'arrowFn:' 'dog'
student.doSth.call(person)(); // 'person' 'arrowFn:' 'person'

8. DOM event handler function call

<button class="button">onclick</button>
<ul class="list">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var button = document.querySelector('button');
button.onclick = function(ev){
console.log(this);
console.log(this === ev.currentTarget); // true
}
var list = document.querySelector('.list');
list.addEventListener('click', function(ev){
console.log(this === list); // true
console.log(this === ev.currentTarget); // true
console.log(this);
console.log(ev.target);
}, false);
</script>

onclick and addEventListener are elements that point to the bound event.

In Summary

If you want to determine the this binding of a running function, you need to find the direct call location of the function. After finding it, you can apply the following four rules in order to determine the binding object of this.

  • new call: bind to the newly created object, note: display the return function or object, the return value is not the newly created object, but the explicitly returned function or object.
  • call or apply (or bind) call: In strict mode, bind to the specified first parameter. In non-strict mode, null and undefined point to the global object (window in browsers), and the rest of the values point to objects wrapped by new Object().
  • A function call on an object: bind to that object.
  • Ordinary function calls: bind to undefined in strict mode, otherwise bind to the global object.

Arrow functions in ES6: The above four standard binding rules will not be used, but this will be determined according to the current lexical scope.

Specifically, arrow functions will inherit the outer function and call this binding (regardless of what this is bound to), if there is no outer function, it is bound to the global object (window in the browser).

DOM event function: generally points to the DOM element to which the event is bound, but in some cases it is bound to the global object.

Unlock 10x development with independent components

Building monolithic apps means all your code is internal and is not useful anywhere else. It just serves this one project. And as you scale to more code and people, development becomes slow and painful as everyone works in one codebase and on the same version.

But what if you build independent components first, and then use them to build any number of projects? You could accelerate and scale modern development 10x.

OSS Tools like Bit offer a powerful developer experience for building independent components and composing modular applications. Many teams start by building their Design Systems or Micro Frontends, through independent components. Give it a try →

0*5qm7YKKCyxLf7sm2?q=20
where-exactly-does-this-point-to-in-javascript-2be1e3fad1fd
An independent product component: watch the auto-generated dependency graph

Learn more


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK