'this' & arrow function

I. this in function

  1. what is this ?

    • all regular functions have this keyword
    • this determines how a function is called on whom!
  2. this in global context

    • this refers gloabl object, no matter in strict mode or not

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // In web browsers, the window object is also the global object:
      console.log(this === window); // true

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

      this.b = "MDN";
      console.log(window.b) // "MDN"
      console.log(b) // "MDN"
  3. this in function context [important]

    There are 7 cases that different function context results in different this reference;

    The value of this depend on how the function is called

    1. this in simple call

      • non-strict mode

        if this is not set explicitly, it default to global object

        1
        2
        3
        4
        5
        6
        7
        function f1() { return this; }

        // In a browser:
        f1() === window; // true

        // In Node:
        f1() === global; // true
      • strict mode

        if this is not set explicitly, the value of this remains undefined

      • 1
        2
        3
        4
        5
        6
        function f2() {
        'use strict'; // see strict mode
        return this;
        }

        f2() === undefined; // true
      • change the value of this, using apply() or call()

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        // An object can be passed as the first argument to call or apply and this will be bound to it.
        var obj = {a: 'Custom'};

        // This property is set on the global object
        var a = 'Global';

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

        whatsThis(); // 'Global'
        whatsThis.call(obj); // 'Custom'
        whatsThis.apply(obj); // 'Custom'
    2. this in bind() method

      • NOTE: bind() doesn’t change the orignal function, instead it return a new function with updated value of this

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        function f() { return this.a; }

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

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

        var o = {a: 37, f: f, g: g, h: h};
        console.log(o.a, o.f(), o.g(), o.h()); // 37,37, azerty, azerty
    3. this in arrow function

      • this cannot be changed, it default to the value of the enclosing lexical context‘s this

      • 1
        2
        3
        var globalObject = this;
        var foo = (() => this);
        console.log(foo() === globalObject); // true
      • About call() & apply()

        • if this arg is passed to call, bind, or apply on invocation of an arrow function it will be ignored.

        • the thisArg still works

        • 1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          // Call as a method of an object
          var obj = {func: foo};
          console.log(obj.func() === globalObject); // true

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

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

          another example: this of arrow function, directly depends on its enclosing context

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          // Create obj with a method bar that returns a function that
          // returns its this. The returned function is created as
          // an arrow function, so its this is permanently bound to the
          // this of its enclosing function. The value of bar can be set
          // in the call, which in turn sets the value of the
          // returned function.
          var obj = {bar: function() {
          var x = (() => this);
          return x;
          }
          };

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

          // Call fn without setting this, would normally default
          // to the global object or undefined in strict mode
          console.log(fn() === obj); // true

          // But caution if you reference the method of obj without calling it
          var fn2 = obj.bar;
          // Then calling the arrow function this is equals to window because it follows the this from bar.
          console.log(fn2()() == window); // true
    4. this in object method

      • When a function is called as a method of an object, its this is set to the object the method is called on.

      • 1
        2
        3
        4
        5
        6
        7
        8
        var o = {
        prop: 37,
        f: function() {
        return this.prop;
        }
        };

        console.log(o.f()); // 37
      • this on the object’s prototype chain

        If the method is on an object’s prototype chain, this refers to the object the method was called on, as if the method were on the object.

    5. this in constructor

      • this is bound to the new object being constructed

      • #A# if constructor doesn’t return: result of new will be the object bound to this

      • #B# if constructor return an object: result of new will be the return value

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        function C() { this.a = 37; }

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

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

        // if an object was returned during construction, then the new object that this was bound to simply gets discarded.

        o = new C2();
        console.log(o.a); // 38
    6. this in DOM event handler

      • this is set to the target element the event fired from

      • this is set to the current element to which the event handler is attached; not the target element

      • this === e.currentTarget

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        // When called as a listener, turns the related element blue
        function bluify(e) {
        // Always true
        console.log(this === e.currentTarget);
        // true when currentTarget and target are the same object
        console.log(this === e.target);
        this.style.backgroundColor = '#A5D9F3';
        }

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

        // Add bluify as a click listener so when the
        // element is clicked on, it turns blue
        for (var i = 0; i < elements.length; i++) {
        elements[i].addEventListener('click', bluify, false);
        }
    7. this in inline event handler

      • this is set to the DOM element where the listener is placed

      • however, only the outer code has its this set this way

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        <!-- 1. this will refer to 'button' -->
        <button onclick="alert(this.tagName.toLowerCase());">
        Show this
        </button>

        <!-- 2. 'this' will refer to 'window' -->
        <button onclick="alert((function() { return this; })());">
        Show inner this
        </button>

II. arrow function

  1. Best suited for non-method functions, ie. not related to method in object

  2. No separate this of its own, using others’

    • An arrow function does not have its own this

    • this value of the enclosing lexical context is used

    • how to find this of enclosing lexical context:

      • check if this is present in current scope;

      • check this is present in its enclosing scope;

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        //---------- version1: works -----------------
        function Person(){
        this.age = 0;

        setInterval(() => {
        this.age++; // |this| properly refers to the Person object
        }, 1000);
        }

        var p = new Person();

        //---------- version2: doesn't work ----------
        function Person() {
        // The Person() constructor defines `this` as an instance of itself.
        this.age = 0;

        setInterval(function growUp() {
        // In non-strict mode, the growUp() function defines `this`
        // as the global object (because it's where growUp() is executed.),
        // which is different from the `this`
        // defined by the Person() constructor.
        this.age++;
        }, 1000);
        }

        var p = new Person();
  3. No binding of arguments

    • Arrow functions do not have their own arguments object.

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      var arguments = [1, 2, 3];
      var arr = () => arguments[0];

      arr(); // 1

      function foo(n) {
      var f = () => arguments[0] + n; // foo's implicit arguments binding. arguments[0] is n
      return f();
      }

      foo(3); // 6
    • using rest parameters is a good alternative to using an arguments object

    • 1
      2
      3
      4
      5
      6
      function foo(n) { 
      var f = (...args) => args[0] + n;
      return f(10);
      }

      foo(1); // 11
  4. When not use arrow function? ==> when need this explicitly

    1. don’t use as methods of object:

      • because we need this, but arrow function doesn’t have its own this

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        'use strict';

        var obj = {
        i: 10,
        b: () => console.log(this.i, this),
        c: function() {
        console.log(this.i, this);
        }
        }

        obj.b(); // prints undefined, Window {...} (or the global object)
        obj.c(); // prints 10, Object {...}
    2. don’t use as function constructor: because we will use new to construct , which need this

    3. don’t add prototype to it: because it doesn’t have this

    4. don’t use in yeild or generators: