Skip to content

Latest commit

 

History

History
282 lines (225 loc) · 4.89 KB

19.this.md

File metadata and controls

282 lines (225 loc) · 4.89 KB

19. this

Problem

https://bigfrontend.dev/quiz/this

Problem Description

What does the code snippet below output by console.log?

const obj = {
  a: 1,
  b: function() {
    console.log(this.a)
  },
  c() {
    console.log(this.a)
  },
  d: () => {
    console.log(this.a)
  },
  e: (function() {
    return () => {
      console.log(this.a);
    }
  })(),
  f: function() {
    return () => {
      console.log(this.a);
    }
  }
}

console.log(obj.a)
obj.b()
;(obj.b)()
const b = obj.b
b()
obj.b.apply({a: 2})
obj.c()
obj.d()
;(obj.d)()
obj.d.apply({a:2})
obj.e()
;(obj.e)()
obj.e.call({a:2})
obj.f()()
;(obj.f())()
obj.f().call({a:2})

Answer

const obj = {
  a: 1,
  b: function() {
    console.log(this.a)
  },
  c() {
    console.log(this.a)
  },
  d: () => {
    console.log(this.a)
  },
  e: (function() {
    return () => {
      console.log(this.a);
    }
  })(),
  f: function() {
    return () => {
      console.log(this.a);
    }
  }
}

console.log(obj.a) // 1
obj.b() // 1
;(obj.b)() // 1

const b = obj.b
b() // undefined

obj.b.apply({a: 2}) // 2
obj.c() // 1
obj.d() // undefined
;(obj.d)() // undefined
obj.d.apply({a:2}) // undefined
obj.e() // undefined
;(obj.e)() // undefined
obj.e.call({a:2}) // undefined
obj.f()() // 1
;(obj.f())() // 1
obj.f().call({a:2}) // 1

Explanation

const obj = {
  a: 1,
  b: function() {
    console.log(this.a)
  },
}

;(obj.b)() // 1

(obj.b)() is identical to obj.b(), because the grouping operator () only changes expression priority and doesn't trigger extra expression value return. (Understanding this, one example at a time)

The semi colon before the left parenthesis ( prevents the JavaScript interpreter from interpreting the parentheses as a function invocation operator. Without the semi-colon, the separator, the JavaScript interpreter will interpret it as obj.b()(obj.b)(). It is the same as:

obj.b();
(obj.b)()

const obj = {
  a: 1,
  b: function() {
    console.log(this.a)
  },
}

const b = obj.b
b() // undefined

b() is a standalone function invocation, so the default binding applies.


const obj = {
  a: 1,
  b: function() {
    console.log(this.a)
  },
}

obj.b.apply({ a: 2 }) // 2

Explicit binding, force the this of obj.b to be the object { a: 2 }.


const obj = {
  a: 1,
  d: () => {
    console.log(this.a)
  },
}

obj.d() // undefined

Arrow functions do not have its own this binding. They adopt the this binding from the enclosed (function or global) scope. For instance,

function foo() {
  return (a) => {
    // `this` here is lexically adopted from `foo()`
    console.log(this.a);
  };
}

var obj1 = {
  a: 2,
};

var bar = foo.call(obj1);
bar(); // 2

The arrow-function defined in foo() lexically captures whatever foo()this is at its call-time.

You Don't Know JS: this & Object Prototypes


const obj = {
  a: 1,
  d: () => {
    console.log(this.a)
  },
}

obj.d.apply({ a: 2 }) // undefined

The lexical this binding of an arrow-function cannot be overridden.

You Don't Know JS: this & Object Prototypes


const obj = {
  a: 1,
  e: (function() {
    return () => {
      console.log(this.a);
    }
  })(),
}

obj.e() // undefined

Since the IIFE is called with a plain, undecorated function reference, its this refers to the global object. The arrow function within the IIFE lexically captures whatever the IIFE's this is at its call-time, therefore obj.e() logs undefined.

Understanding this, one example at a time


const obj = {
  a: 1,
  e: (function() {
    return () => {
      console.log(this.a);
    }
  })(),
}

obj.e.call({ a: 2 }) // undefined

The lexical this binding of an arrow-function cannot be overridden.


const obj = {
  a: 1,
  f: function() {
    return () => {
      console.log(this.a);
    }
  }
}

obj.f()() // 1

We first call obj.f() and it returns an arrow function. The arrow function lexically captures whatever f()'s this is at its call-time. Since f() is this-bound to obj, the returned arrow function is also this-bound to obj.


obj.f().call({ a: 2 }) // 1

The lexical this binding of an arrow-function cannot be overridden.