xpt
xpt

Reputation: 22994

Exported File Scope Variables of ES6 Javascript

Following up on What is file scope in javascript, from which I know that there might or might not be a so-called File Scope Variables, however, I do remember reading such term somewhere but couldn't find it any more, except the Q&A of What is file scope in javascript. Anyway,

I want to know what's exact behavior definition for such exported file scope variables.

Because I was trying to toggle my bot dynamically but wasn't able to, eliminating factors one by one, and it finally came down on me it is because of such "exported file scope variables", and me not understanding their behavior. Take a look at the following extremely simplified bot application:

VarTestFileA.js

function nextBot() {
  BotC = !BotC
  return BotA[BotC]
}

function logBot() {
  console.log("I:", bot)
}

const BotA = {true: {"token": 2}, false: {"token":3}}
let BotC = true

var bot = BotA[BotC]

module.exports = {
  bot,
  nextBot,
  logBot,
}

VarTestFileB.js

const bt = require('./VarTestFileA')

console.log(bt.bot)

bt.bot = bt.nextBot()
bt.logBot()
console.log("O:", bt.bot)

bt.bot = bt.nextBot()
bt.logBot()
console.log("O:", bt.bot)

bt.bot = bt.nextBot()
bt.logBot()
console.log("O:", bt.bot)

You may know (even without running it) that no matter how I did, the bt.bot cannot be toggled. Here is the output:

$ node VarTestFileB.js
{ token: 2 }
I: { token: 2 }
O: { token: 3 }
I: { token: 2 }
O: { token: 2 }
I: { token: 2 }
O: { token: 3 }

Also, I tried to toggle bot from within VarTestFileA.js, it works within it, but console.log("O:", bt.bot.token) never shows the updated value. All and all,

It all comes down to exact behavior definition of such exported file scope variables, because if putting them in the same file, it runs perfectly fine. Hence the question.

Upvotes: 0

Views: 127

Answers (1)

Mosia Thabo
Mosia Thabo

Reputation: 4267

The behavior is quite straight forward, and quite interesting at the same time. The statement const bt = require('./VarTestFileA') creates an object copy of the exported object, and where we used variables (e.g bot) - values are passed instead of reference, but where we passed a Function then a reference is passed because functions are objects in JS.

From VarTestFileA.js we exported { bot, nextBot, logBot } so dt in essence is actually equal to:

dt = {
  bot : bot, //copy of old bot = BotA[BotC] which equals {"token": 2} 
  nextBot: nextBot, //reference to nextBot() which has access to bot in file A
  nextBot: logBot , //reference to logBot () which has access to bot in file A
}

Now coming to VarTestFileB.js where we print and try to understand the behavior, let's look at how each statement behaves:

First Statement:

console.log(bt.bot) will print {"token": 2} because bot was passed by value and not reference.

Second Statement:

bt.bot = bt.nextBot() this actually changes value of dt.bot and not the true bot in file A. So bt.bot will have the toggled value, but the actual bot declared in file A is still the same because it hasn't been changed. That now takes us to the third statement:

Third Statement:

bt.logBot(), this prints the initial value of bot in file A. Because it hasn't been changed.

So the real answer here is, based on this behavior we can declare that if we hold references of the variables in File A, then we can change them from another file. But can I really provide evidence for this? Well! yes, let's see how below


To test this, let's create another method in File A;

function getBot(){
    return bot;
}

And then modify the nextBot as follows:

function nextBot() {
    BotC = !BotC
    bot = BotA[BotC]
}

and then export references to these methods;

module.exports = {
    getBot,
    nextBot,
    logBot,
}

So we can now perform the same experiments and try to print out the old test from the question:

const bt = require('./VarTestFileA')
console.log(bt.getBot())

bt.nextBot()
bt.logBot()
console.log("O:", bt.getBot())

bt.nextBot()
bt.logBot()
console.log("O:", bt.getBot())

bt.nextBot()
bt.logBot()
console.log("O:", bt.getBot())

And the output is as follows:

{ token: 2 }
I: { token: 3 }
O: { token: 3 }
I: { token: 2 }
O: { token: 2 }
I: { token: 3 }
O: { token: 3 }

The results are interesting because now the bot from File A seems to be toggling as expected.


In Conclusion, the issue is passing properties either by reference or value. bot from your initial code is passed as by value, it's not a complex item or it's not declared as type Object but holds a simple object as value. So there's no way to pass bot by reference. I stand to be corrected but I don't think variables that holds a primitive value like a Boolean in JavaScript or simple objects as values can be passed by reference. But functions are complex types, meaning they are Objects, hence they are passed by reference.

I hope you find this in order.

Upvotes: 1

Related Questions