difference between function context (this) in node.js and browser - javascript

I am aware of how "this" works in a browser context , and how its value changes in different scenarios like when using arrow functions how the function is invoked.
I printed out "this" in different scenarios for node js (express js, to be more specific), and it is containing a lot more data - including path names etc
My question is :
1. are the rules concerning 'this' exactly the same for node.js ?
2. could any one explain the node.js 'this' object properties or point me to a simple article.
Thank you!

There are no different rules for this in a browser vs. node.js. The rules are set by the ECMAScript standards and both the browser's Javascript implementation and the one in node.js follow the same ECMAScript standards.
What you are probably looking at is a "default" value for this in some particular context. In a browser, you are probably looking at a default value for this that may be the window object. In node.js, if you see filenames, you may be looking at a module handle as the default value for this or the global object.
To help you more specifically, we would need to see the code around where you were examining the value of this in each environment and also know whether you were running in strict mode or not.
In most cases, this is not used with just a default value, but rather a specific object that the this value is set to. For example, if you are calling something like:
obj.method();
Then, inside the implementation of method, the Javascript interpreter will set the value of this to obj. This is a part of the object oriented nature of Javascript.

This this object is whatever the global object is in that context. In node that is the process object.

I observed a difference between this in a module when running on node (tests) and a browser (production).
in tests:
the following type of code worked fine when run by tests:
export function A()
{
}
export function B()
{
// NOTE: DON'T DO THIS. prepending "this." is not needed and might break.
this.A();
}
But on production it would throw:
TypeError: Cannot read property 'A' of undefined
However this is NOT a difference between node + browser/webview but rather a difference between production code (production build of bundle.js via webpack v4) and code running in tests.
With a debug build of bundle.js this would point to the module (so an object containing exported module symbols)
eg:
{
A : [Function: A]
B : [Function: B]
}
Whereas in a release build of bundle.js this returns undefined
This difference of behaviour is caused by webpacks concatenateModules optimization.

Related

Why is the process.env object empty but I can access it's properties?

I have just come across the idea of using environment variables in a front end React codebase I am looking at. When I run the React app locally I can see that when I console.log process.env.VALUE, then this value will be logged out in the browser console.
However if I console.log(process.env) I only see an empty object. I've seen other answers for this on Stack Overflow but I couldn't understand them. It doesn't make sense to me how I can access the properties on process.env but I can't log process.env out. Isn't it just an object which contains all the environment variables that are set?
If someone can explain this simply that would be appreciated. thanks.
The reason for that is because React naturally uses webpack.DefinePlugin when you are building your production build. That library basically makes your process.env variable to essentially be 'placeholders' - meaning that all of the values are replaced during build time. That's why process.env returns {}, while process.env.NODE_ENV returns the correct one for example.
Slightly off-topic, but in Next.js (a React framework), all environment variables are also replaced during build time (for security purposes). That means that process.env is not a normal JavaScript object in Next.js. Internally, they use webpack.DefinePlugin as well.
As an addition for the answer, the browser does not have access to process.env. React is basically compiled to HTML/CSS/JS - that means the application is just a static site. process.env is a Node.js thing.
Example:
Imagine you have an environment variable called FOO with value BAR in whatever environment you desire when you build your app.
You compile the application, React uses webpack.DefinePlugin to transform your FOO into process.env.FOO. It is literally process.env.FOO. The compiler does not create an object called process.env with an attribute FOO. Literally, the process.env.FOO is attached in the window object of your app.
You assume that it's on process.env.FOO, and it does exist in your application during runtime.
You falsely try to access process.env object (it is not and have never been an object) since the beginning of the compilation, and that's why it returns an empty object -- it simply is never an object to begin with for the reasons that I have explained in the above.
References:
DefinePlugin by Webpack
React.js Environment Variables
Next.js Environment Variables

How can a JavaScript program know if a global property name was defined by ECMA-262?

Consider a hypothetical function called isEcmaGlobal that returns true for string names of ECMA-262 globals.
$ node
> isEcmaGlobal('Array')
true
> isEcmaGlobal('process')
false
How would one design this function?
Lets be careful here. We need to ask another question before this answer will make sense: Are we categorizing globals by what ECMA-262 ("the standard") says now, or by the standard governing our program's runtime?
Put another way: Should multiple machines see the same answers?
Going by the Current Standard
Let's say we want isEcmaGlobal to return true in terms of what the standard says right now. I won't show any code here because if you care about this kind of problem, then you probably already thought to download or scrape standard global names.
Going by the Current Runtime
Now let's assume we want isEcmaGlobal to return true in terms of offline information available in the current runtime. It's not as easy as iterating over the "own" property names of the global object and checking them against a hard-coded array like ['Array', 'Object', ...]. If we did that, then we'd have to verify our array is correct. We can't verify hard-coded data because it assumes isEcmaGlobal is already written for that exact runtime! We cannot paste in global names from ECMA-262, because we don't know if the version of that document is in parity with the runtime. We also can't check the Internet because we assumed an offline solution.
So we can't hard code, but we can try something else. I'll use Node.js v14.16.0 and its built-in vm module. This program exploits vm's need to provide standard globals in a new evaluator. We tell vm to evaluate code using an empty global object, but that global object simply won't be empty.
const vm = require('vm');
function isEcmaGlobal(identifier) {
try {
// Lol, what security?
vm.runInContext(identifier, vm.createContext({}))
return true;
} catch (e) {
return false;
}
}
Couple of things here.
First, a warning: This example uses unsanitized code execution for illustration and brevity. Do not put it on your clipboard with intent to take a shortcut.
Second, you can see our goal is to create a fresh evaluator with only standard globals defined, and no visibility into what's going on outside of the evaluator (eval does not do either of these things). If we get that evaluator, then you can just toss Object.getOwnPropertyNames(new Function('return this')()) into it and we all go home.
But we're making a predicate, so assuming the "lean" global object is correct, evaluation will throw a ReferenceError for non-standard identifiers.
Unfortunately, somehting isn't right.
> isEcmaGlobal('Array')
true
> isEcmaGlobal('process')
false // yes!
> isEcmaGlobal('global')
false // yes!!
> isEcmaGlobal('console')
true // no!!!
Darn, we still have a Node global for some reason. So at this point you could say I'm making this all too complicated because Node.js doesn't add that many globals, or that I missed a configuration option or existing predicate. You'd be right on all counts, but that's missing the point of the question. The question started with "Can a JavaScript program know...?", and my use of Node is meant to illustrate a way to approach the problem without hard-coding standard global names and creating a contradiction. Even if Node.js added only one non-standard global, the problem remains if we can't get a clean evaluator.
Recap
To implement isEcmaGlobal in some JavaScript runtime, that runtime needs to give you a way to run a bare-bones JS evaluator, free of all host-specific extensions. eval doesn't count. Make it such that that you only accept identifiers, and then evaluate those identifiers. Interpret an exception as reason to return false.

var doesn't attach to global

I am trying to understand this keyword. but the problem is with the node environment. I am getting the expected behavior in Chrome Developer tool but the same code isn't working fine in the node environment.
When we create a var in the global context, it is supposed to be inside the global (node) or window (browser) but in node environment, it doesn't get attached to document.
I am just testing a simple 3 lines of code which works totally fine in chrome.
This is for Node environment
var color = 'red';
console.log(this.color);
console.log(global.color)
and this is for Browser which works fine
var color = 'red';
console.log(this.color);
console.log(window.color)
For the node environment, I am receiving undefined which is not expected.
Here's a software development rule: don't rely on variables sticking on this, global, or module-related objects. Scope can vary and lead to unexpected behavior and bugs. Use explicit (this|global|module.exports).varName bindings.
But if you just want to understand how things work in Node:
Code is wrapped into an IIFE when executed, setting the this value to module.exports (not global).
Access to global is persistent across modules, so if you write global.foo='foo' in a module then require it in bar.js, global.foo will be set to 'foo' in bar.js. It is discouraged to directly use global for most use cases - stick to exports and require.
According to the specs, var is not supposed to make things stick to global: there are some exceptions (see below), but you should not rely on that when writing code
Related questions:
Do let statements create properties on the global object?
Meaning of “this” in node.js modules and functions

Nodejs uses variable assignment to load modules

Most languages use 'import' directives to load other module code, like
java -
import a.b.c
elisp -
(load a)
python -
from a import b
But, why does nodejs use a variable expression to load other module functions like
var a = require('a')
i see, most IDEs for javascript like tern.js-emacs, nodeclipse are not able to do source code lookup (for loaded modules) properly because the IDE has to run the code (or) do eval to find out, what properties a loaded module object contains.
You could say JS belongs to a category of languages where the idea that everything is an object on equal footing is part of the "philosophy" that has guided its development. Node's require is a function (an object) supplied by the environment, as is the module object. This pattern is called the Common JS format.
You actually don't have to assign the result of the require function to a variable. It's rare in practice, but the node module you're calling on could just be invoked to cause an action to take place, for example one might require sugar.js which alters some of the native objects but has no methods of its own to offer, so there would be no point in assigning the return value (which is the module.exports object that was supplied during that module's execution).
A more common example of not assigning a module to a variable is when one uses require just to grab some property off the module -- e.g. var x = require('module').methodOfInterest. Similarly, some modules return a constructor, so you may sometimes see var instance = new (require('ConstructorModule'))(options) (which is ugly in my opinion; requires should generally be grouped at the top of a file and acted on only afterwards).
Note: There's really no concrete answer to your question so odds are high that it will get closed as SO-inappropriate.

Possible to enumerate or access module-level function declarations in NodeJs?

In Node.js, if I load a module which contains code in module-scope like:
this["foo"] = function() { console.log("foo"); }
...then I appear to get a globally available function that I can call just by saying foo() from any code using the module. It can be seen as one of the printed items with Object.getOwnPropertyNames(this).
However, if I put the following in module scope instead:
function foo() { console.log("foo"); }
...then it produces a function which can similarly be called within that module as foo(), but is invisible outside of it (e.g. does not show up as one of the items with Object.getOwnPropertyNames(this)).
I gather this is a change in runtime behavior from what's done in browsers. A browser seems to poke everything into global scope by default (and for years people have had to consciously avoid this by wrapping things up in anonymous functions/etc.)
My question is whether NodeJs has some secret way of interacting with these declarations outside of the module in which they are declared BESIDES using exports.(...) = (...). Can they be enumerated somehow, or are they garbage collected as soon as they are declared if they're not called by a module export? If I knew what the name of such a function was going to be in advance of loading a module...could I tell Node.js to "capture it" when it was defined?
I'm not expecting any such capabilities to be well-documented...but perhaps there's a debugger feature or other system call. One of the best pointers would be to the specific code in the Node.js project where this kind of declaration is handled, to see if there are any loopholes.
Note: In researching a little into V8 I saw that a "function definition" doesn't get added to the context. It's put into an "activation object" of the "execution context", and cannot be programmatically accessed. If you want some "light reading" I found:
http://coachwei.sys-con.com/node/676031/mobile
http://perfectionkills.com/understanding-delete/
if you fill in exports.foo = foo; at the end of your file it will be available in other files in node, assuming that you do var myFile = require('myFile.js') with the file name and you call the function via myFile.foo(); You can even rename the function for outside use in exports and set whatever you want to call the package when you use require.
BTW you can enumerate these functions just like you do on any JSON object (ie for k in ...)
This is impossible in node without more advanced reflection tools like the debugger.
The only way to do this would be to use __parent__ which was removed due to security issues and other things (hard to optimize, not standard to begin with) . When you run the script those variables become closed under the module. You can not access them elsewhere without explicitly exporting them.
This is not a bug, it's by design. It's just how node works. see this closely related question.
If this sort of reflection was available without tools like the debugger it would have been extremely hard to optimize the source code (at least the way v8 works) , meaning the answer to your question is no. Sorry.

Categories