1

In a javascript file, when I declare a function using function keyword, I can placed my function after my caller function, something like

// test.js
function myCaller() {
   foo('hello world');    // this works!
   this.foo('hello world');  // this works!
}
function foo(text) {
   console.log('foo called!', text);
}
myCaller()

but if I turned the foo into an arrow function, and placed it at same position, then in myCaller function, it said foo is not defined, also it won't work if I use this keyword to locate the foo function, which I assume the this refers to global/document level

// test.js
function myCaller() {
   foo('hello world');    // es-lint warning: 'foo' was used before it was defined
   this.foo('hello world');  // compilation error: foo is not defined
}
const foo = (text) => {
   console.log('foo called!', text);
}
myCaller();

- I was wrong, I thought the 2nd approach without using this does not work because of javascript compilation error of not defined, but it was actully my eslint error - 'foo' was used before it was defined, does it mean it's not recommend to do this way?

Why is that and does it mean we have to declare the arrow function always above caller function? Is there any alternative way to solve that?

const foo = (text) => {
   console.log('foo called!', text);
}
// test.js
function myCaller() {
   foo('hello world');    // this works! no es-lint warning now
   this.foo('hello world');  // no compilation error but there is a run-time error : 'this.foo is not a function'
}

myCaller();

In addition, I found when declare the arrow function inside a javascript class, it would work with this keyword only with caller function being arrow function as well, if caller function is with function keyword, this will not work...

// myTest.js
class myTest {
   myCaller() {
      foo('hello world');    // compilation error: foo is undefined
   }
   myCaller2 = () => {
     this.foo('hello world'); //this works!
   }
   foo = (text) => {
      console.log('foo called!', text);
   }
}
new myTest().myCaller();
new myTest().myCaller2();
Drex
  • 3,346
  • 9
  • 33
  • 58
  • 2
    nothing to do with arrow, has to do with scope of `const` – Jaromanda X Aug 23 '20 at 05:49
  • https://stackoverflow.com/questions/54933689/arrow-function-hoisting-in-node – Baruch Mashasha Aug 23 '20 at 05:51
  • 3
    also, `this.foo` would subsequently fail even if const foo was above myCaller - since const and let variables are not added to the "global" object - `this` would likely be the global object (window on a browser) in your code – Jaromanda X Aug 23 '20 at 05:52
  • @JaromandaX should I remove the `const`? I cannot remove the `const`...if I remove the `const`, there is compile error said `foo is not defined` – Drex Aug 23 '20 at 05:55
  • @BaruchMashasha thanks for the link, but I don't think that helps here, as I am not executing `myCaller` function, as it's already generated compilation error, in addition, why it works when it uses `function` keyword but just not for arrow function? – Drex Aug 23 '20 at 06:02
  • @JaromandaX, yes I assume the `this` would be global object as well, then why when declaring the arrow function, it complains `foo is not defined`, but when declares using `function` keyword then using `this.foo`, it works? – Drex Aug 23 '20 at 06:06
  • `foo('hello world')` in your second example does *not* produce any error…! – deceze Aug 23 '20 at 06:13
  • Arrow functions are defined in scope. See this question. https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback – sin tribu Aug 23 '20 at 06:24
  • @deceze, sorry my bad, it was actually an eslint warning "foo was used before it was defined" – Drex Aug 23 '20 at 06:35
  • `function foo`, like `var foo` in the global scope will be part of the global scope ... `let` or `const` are not – Jaromanda X Aug 23 '20 at 06:36
  • Thanks @JaromandaX, if `let` or `const` won't be considered at scope level, then why it works when working within `class` here? Or do you mean they wouldn't work with `global` scope specifically? – Drex Aug 23 '20 at 06:48
  • `let foo` in the global scope won't "create" a global.foo (i.e. `this.foo` in the global scope) whereas `var foo` or `function foo` does – Jaromanda X Aug 23 '20 at 06:49

1 Answers1

2

Any variables (except let and const variables) that you declare or functions that you define in global context (say, directly in test.js) will be attached to Window object. So, when you write,

// test.js
function myCaller() {
   foo('hello world');    // this works!
   this.foo('hello world');  // this works!
}
function foo(text) {
   console.log('foo called!', text);
}
myCaller()

Both myCaller and foo are attached to window object as properties. Now you can refer to them either directly like foo()(this is implicit here) or this.foo() or even window.foo(). Since js uses hoisting, these variables or functions are first attached to the context and then starts executing.

But const and let are not attached to the Window object (otherwise they would be accessible everywhere, behaving exactly like var). So when you write

// test.js
function myCaller() {
   foo('hello world');
   this.foo('hello world');
}
const foo = (text) => {
   console.log('foo called!', text);
}
myCaller();

Javascript engine scans through the entire script to see if there are any variables or functions defined. In this hoisting phase, if it finds a const or let, it allocates memory for them but won't make them part of Window object. So, when you are referring to this in global context, it is referring to window object and foo is not a property of window object. That is why, even if you put const foo above myCaller definition, it won't work.

In case of class, if you try to call foo() directly without referring to this, it tries to access it from the enclosing contexts, where it is not defined in your example. So it throws error. If you define another foo() outside the class with var or directly as foo = (text) => ..., it will work.

(The this may not be always referring to global context. Functions and classes can have their own custom context, and this would refer to that context. But when no custom context is defined by the functions or classes, the global context will be the one the this keyword would be referring to. The this is undefined for functions and classes by default in strict mode. There are several such caveats to be taken into consideration.)

chethan7
  • 166
  • 6
  • You should explain why `this` inside `myCaller` is (or might not) be the global object… – deceze Aug 23 '20 at 08:05
  • 1
    `foo()` inside the class isn’t necessarily looked up from a *global* context, just from any surrounding scope. – deceze Aug 23 '20 at 08:07
  • 1
    JavaScript is also *compiled*, just on an as-needed basis. It can still throw compilation errors; syntax errors are thrown in the compilation/parsing phase. That’s distinct from a runtime error. – deceze Aug 23 '20 at 08:09
  • Thank you @chethan7! It explained very well! One thing I would like to understand betteris, in case of class, if I declare `foo()` outside my class, then I can call `foo()` directly inside my class method, does it mean that now it's because the `foo()` is attached to the `global windows` context and that's why I can call it inside my class method? – Drex Aug 25 '20 at 03:00
  • And you mentioned `That is why, even if you put const foo above myCaller definition, it won't work.` Actually if I directly call the `foo()` without `this`, it did work...It's just not work when I call `this.foo()`, but since you mentioned, when met with `const` or `let`, JS allocates memory for them but not make them part of Window object, that'd prob explain why `this.foo()` not work because currently the `this` refers to windows context, hwever, why calling without `this` would work this time? – Drex Aug 25 '20 at 03:04