Featured image of post 8 Different Places “this” Can Point to in JavaScript

8 Different Places “this” Can Point to in JavaScript

Overview

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.

Global context

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

1
2
3
4
5
this === window // true
'use strict'
this === window;
this.name = 'dog';
console.log(this.name); // dog

Function context

1
2
3
4
5
6
var name = 'window';
var doSth = function(){
    console.log(this === window);
    console.log(this.name);
}
doSth(); // true, '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.

1
2
3
4
5
6
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.doSth2 are both undefined.

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

1
2
3
4
5
6
7
'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 { 类比;类推;比喻 }:

1
2
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.

1
2
3
4
5
6
7
8
9
var name = 'dog';
setTimeout(function(){
    console.log(this === window);
    console.log(this.name);
}, 0);// true, dog

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

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

Object function (method) invocation pattern

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
var name = 'window';
var doSth = function(){
    console.log(this.name);
}
var student = {
    name: 'dog',
    doSth: doSth,
    other: {
        name: 'other',
        doSth: doSth,
    }
}

student.doSth(); // 'dog'
// call like this
student.doSth.call(student);

student.other.doSth(); // 'other'
// call like this
student.other.doSth.call(student.other);

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).

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

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

1
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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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'

Constructor call pattern

1
2
3
4
5
6
7
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.

Calling Patterns in the Prototype Chain

1
2
3
4
5
6
7
8
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.

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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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'

DOM event handler function call

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<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>

In onclick and addEventListener, this points to the elements of 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.

References

Licensed under CC BY-NC-SA 4.0
Last updated on May 05, 2023 20:37 CST
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy