I\'ve just seen a weird behaviour of the this
keyword in NodeJS environment. I\'m listing them with code. I\'ve run this codes with NodeJS v6.x
, wi
Very interesting:
var JSON = require('circular-json');
console.log('1) ' + JSON.stringify(this, null, 2));
(function(){
console.log('2) ' + JSON.stringify(this, null, 2));
}());
(function(){
'use strict';
console.log('3) ' + JSON.stringify(this, null, 2));
}());
will produce:
1) {}
2) {
"global": "~",
"process": {
"title": "node",
"version": "v6.9.1",
"moduleLoadList": [
"Binding contextify",
"Binding natives",
"NativeModule events",
"NativeModule util",
"Binding uv",
"NativeModule buffer",
"Binding buffer",
"Binding util",
"NativeModule internal/util",
"NativeModule timers",
"Binding timer_wrap",
"NativeModule internal/linkedlist",
"NativeModule assert",
"NativeModule internal/process",
"Binding config",
"NativeModule internal/process/warning",
"NativeModule internal/process/next_tick",
"NativeModule internal/process/promises",
"NativeModule internal/process/stdio",
"Binding constants",
"NativeModule path",
"NativeModule module",
"NativeModule internal/module",
"NativeModule vm",
"NativeModule fs",
"Binding fs",
"NativeModule stream",
"NativeModule _stream_readable",
"NativeModule internal/streams/BufferList",
"NativeModule _stream_writable",
"NativeModule _stream_duplex",
"NativeModule _stream_transform",
"NativeModule _stream_passthrough",
"Binding fs_event_wrap",
"NativeModule console",
"Binding tty_wrap",
"NativeModule tty",
"NativeModule net",
"NativeModule internal/net",
"Binding cares_wrap",
"Binding tcp_wrap",
"Binding pipe_wrap",
"Binding stream_wrap",
"Binding signal_wrap"
],
"versions": {
"http_parser": "2.7.0",
"node": "6.9.1",
"v8": "5.1.281.84",
"uv": "1.9.1",
"zlib": "1.2.8",
"ares": "1.10.1-DEV",
"icu": "57.1",
"modules": "48",
"openssl": "1.0.2j"
},
"arch": "x64",
"platform": "linux",
"release": {
"name": "node",
"lts": "Boron",
"sourceUrl": "https://nodejs.org/download/release/v6.9.1/node-v6.9.1.tar.gz",
"headersUrl": "https://nodejs.org/download/release/v6.9.1/node-v6.9.1-headers.tar.gz"
},
"argv": [
"/usr/local/bin/node",
"/home/froth/freelancer-projects/thistest.js"
],
"execArgv": [],
"env": {
"NVM_DIR": "/home/froth/.nvm",
"LD_LIBRARY_PATH": "/opt/opencascade/lib",
"CSF_UnitsDefinition": "/opt/opencascade/src/UnitsAPI/Units.dat",
"CSF_GraphicShr": "/opt/opencascade/lib/libTKOpenGl.so",
"CSF_EXCEPTION_PROMPT": "1",
"LANG": "de_DE.UTF-8",
"PROFILEHOME": "",
"DISPLAY": ":0",
"SHELL_SESSION_ID": "09b6f0f3b1d94c5f8aba3f8022075677",
"NODE_PATH": "/usr/lib/node_modules",
"COLORTERM": "truecolor",
"NVM_CD_FLAGS": "",
"MOZ_PLUGIN_PATH": "/usr/lib/mozilla/plugins",
"CSF_IGESDefaults": "/opt/opencascade/src/XSTEPResource",
"CSF_XCAFDefaults": "/opt/opencascade/src/StdResource",
"XDG_VTNR": "1",
"PAM_KWALLET5_LOGIN": "/tmp/kwallet5_froth.socket",
"CSF_STEPDefaults": "/opt/opencascade/src/XSTEPResource",
"XDG_SESSION_ID": "c2",
"CSF_XSMessage": "/opt/opencascade/src/XSMessage",
"USER": "froth",
"DESKTOP_SESSION": "/usr/share/xsessions/awesome",
"GTK2_RC_FILES": "/home/froth/.gtkrc-2.0",
"PWD": "/home/froth/freelancer-projects",
"HOME": "/home/froth",
"XDG_SESSION_TYPE": "x11",
"CSF_PluginDefaults": "/opt/opencascade/src/StdResource",
"XDG_DATA_DIRS": "/usr/local/share/:/usr/share/:/var/lib/snapd/desktop",
"NVM_IOJS_ORG_MIRROR": "https://iojs.org/dist",
"KONSOLE_DBUS_SESSION": "/Sessions/1",
"XDG_SESSION_DESKTOP": "",
"CSF_StandardDefaults": "/opt/opencascade/src/StdResource",
"CSF_StandardLiteDefaults": "/opt/opencascade/src/StdResource",
"MMGT_CLEAR": "1",
"KONSOLE_DBUS_WINDOW": "/Windows/1",
"CSF_UnitsLexicon": "/opt/opencascade/src/UnitsAPI/Lexi_Expr.dat",
"GTK_MODULES": "canberra-gtk-module",
"MAIL": "/var/spool/mail/froth",
"NVM_RC_VERSION": "",
"CSF_XmlOcafResource": "/opt/opencascade/src/XmlOcafResource",
"TERM": "xterm-256color",
"SHELL": "/bin/bash",
"KONSOLE_DBUS_SERVICE": ":1.23",
"XDG_SESSION_CLASS": "user",
"XDG_SEAT_PATH": "/org/freedesktop/DisplayManager/Seat0",
"XDG_CURRENT_DESKTOP": "",
"QT_LINUX_ACCESSIBILITY_ALWAYS_ON": "1",
"KONSOLE_PROFILE_NAME": "Shell",
"CASROOT": "/opt/opencascade",
"NVM_NODEJS_ORG_MIRROR": "https://nodejs.org/dist",
"COLORFGBG": "15;0",
"XDG_SEAT": "seat0",
"SHLVL": "2",
"LANGUAGE": "",
"WINDOWID": "29360134",
"LOGNAME": "froth",
"DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus",
"XDG_RUNTIME_DIR": "/run/user/1000",
"CSF_MDTVTexturesDirectory": "/opt/opencascade/src/Textures",
"XAUTHORITY": "/home/froth/.Xauthority",
"XDG_SESSION_PATH": "/org/freedesktop/DisplayManager/Session1",
"PATH": "/home/froth/.gem/ruby/2.3.0/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/snap/bin:/usr/lib/jvm/default/bin:/opt/opencascade/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl",
"CSF_LANGUAGE": "us",
"CSF_SHMessage": "/opt/opencascade/src/SHMessage",
"OLDPWD": "/home/froth",
"_": "/usr/local/bin/node"
},
"pid": 4658,
"features": {
"debug": false,
"uv": true,
"ipv6": true,
"tls_npn": true,
"tls_alpn": true,
"tls_sni": true,
"tls_ocsp": true,
"tls": true
},
"_needImmediateCallback": false,
"execPath": "/usr/local/bin/node",
"debugPort": 5858,
"_events": {
"SIGWINCH": [
null,
null
]
},
"_eventsCount": 4,
"domain": null,
"_exiting": false,
"config": {
"target_defaults": {
"cflags": [],
"default_configuration": "Release",
"defines": [],
"include_dirs": [],
"libraries": []
},
"variables": {
"asan": 0,
"debug_devtools": "node",
"force_dynamic_crt": 0,
"gas_version": "2.23",
"host_arch": "x64",
"icu_data_file": "icudt57l.dat",
"icu_data_in": "../../deps/icu-small/source/data/in/icudt57l.dat",
"icu_endianness": "l",
"icu_gyp_path": "tools/icu/icu-generic.gyp",
"icu_locales": "en,root",
"icu_path": "deps/icu-small",
"icu_small": true,
"icu_ver_major": "57",
"node_byteorder": "little",
"node_enable_d8": false,
"node_enable_v8_vtunejit": false,
"node_install_npm": true,
"node_module_version": 48,
"node_no_browser_globals": false,
"node_prefix": "/",
"node_release_urlbase": "https://nodejs.org/download/release/",
"node_shared": false,
"node_shared_cares": false,
"node_shared_http_parser": false,
"node_shared_libuv": false,
"node_shared_openssl": false,
"node_shared_zlib": false,
"node_tag": "",
"node_use_bundled_v8": true,
"node_use_dtrace": false,
"node_use_etw": false,
"node_use_lttng": false,
"node_use_openssl": true,
"node_use_perfctr": false,
"node_use_v8_platform": true,
"openssl_fips": "",
"openssl_no_asm": 0,
"shlib_suffix": "so.48",
"target_arch": "x64",
"uv_parent_path": "/deps/uv/",
"uv_use_dtrace": false,
"v8_enable_gdbjit": 0,
"v8_enable_i18n_support": 1,
"v8_inspector": true,
"v8_no_strict_aliasing": 1,
"v8_optimized_debug": 0,
"v8_random_seed": 0,
"v8_use_snapshot": true,
"want_separate_host_toolset": 0
}
},
"stdout": {
"connecting": false,
"_hadError": false,
"_handle": {
"bytesRead": 0,
"_externalStream": {},
"fd": 9,
"writeQueueSize": 0,
"owner": "~process~stdout"
},
"_parent": null,
"_host": null,
"_readableState": {
"objectMode": false,
"highWaterMark": 16384,
"buffer": {
"head": null,
"tail": null,
"length": 0
},
"length": 0,
"pipes": null,
"pipesCount": 0,
"flowing": null,
"ended": false,
"endEmitted": false,
"reading": false,
"sync": true,
"needReadable": false,
"emittedReadable": false,
"readableListening": false,
"resumeScheduled": false,
"defaultEncoding": "utf8",
"ranOut": false,
"awaitDrain": 0,
"readingMore": false,
"decoder": null,
"encoding": null
},
"readable": false,
"domain": null,
"_events": {},
"_eventsCount": 3,
"_writableState": {
"objectMode": false,
"highWaterMark": 16384,
"needDrain": false,
"ending": false,
"ended": false,
"finished": false,
"decodeStrings": false,
"defaultEncoding": "utf8",
"length": 0,
"writing": false,
"corked": 0,
"sync": false,
"bufferProcessing": false,
"writecb": null,
"writelen": 0,
"bufferedRequest": null,
"lastBufferedRequest": null,
"pendingcb": 1,
"prefinished": false,
"errorEmitted": false,
"bufferedRequestCount": 0,
"corkedRequestsFree": {
"next": null,
"entry": null
}
},
"writable": true,
"allowHalfOpen": false,
"destroyed": false,
"_bytesDispatched": 6,
"_sockname": null,
"_writev": null,
"_pendingData": null,
"_pendingEncoding": "",
"server": null,
"_server": null,
"columns": 84,
"rows": 84,
"_type": "tty",
"fd": 1,
"_isStdio": true
},
"stderr": {
"connecting": false,
"_hadError": false,
"_handle": {
"bytesRead": 0,
"_externalStream": {},
"fd": 11,
"writeQueueSize": 0,
"owner": "~process~stderr"
},
"_parent": null,
"_host": null,
"_readableState": {
"objectMode": false,
"highWaterMark": 16384,
"buffer": {
"head": null,
"tail": null,
"length": 0
},
"length": 0,
"pipes": null,
"pipesCount": 0,
"flowing": null,
"ended": false,
"endEmitted": false,
"reading": false,
"sync": true,
"needReadable": false,
"emittedReadable": false,
"readableListening": false,
"resumeScheduled": false,
"defaultEncoding": "utf8",
"ranOut": false,
"awaitDrain": 0,
"readingMore": false,
"decoder": null,
"encoding": null
},
"readable": false,
"domain": null,
"_events": {},
"_eventsCount": 3,
"_writableState": {
"objectMode": false,
"highWaterMark": 16384,
"needDrain": false,
"ending": false,
"ended": false,
"finished": false,
"decodeStrings": false,
"defaultEncoding": "utf8",
"length": 0,
"writing": false,
"corked": 0,
"sync": true,
"bufferProcessing": false,
"writecb": null,
"writelen": 0,
"bufferedRequest": null,
"lastBufferedRequest": null,
"pendingcb": 0,
"prefinished": false,
"errorEmitted": false,
"bufferedRequestCount": 0,
"corkedRequestsFree": {
"next": null,
"entry": null
}
},
"writable": true,
"allowHalfOpen": false,
"destroyed": false,
"_bytesDispatched": 0,
"_sockname": null,
"_writev": null,
"_pendingData": null,
"_pendingEncoding": "",
"server": null,
"_server": null,
"columns": 84,
"rows": 84,
"_type": "tty",
"fd": 2,
"_isStdio": true
},
"stdin": {
"connecting": false,
"_hadError": false,
"_handle": {
"bytesRead": 0,
"_externalStream": {},
"fd": 12,
"writeQueueSize": 0,
"owner": "~process~stdin",
"reading": false
},
"_parent": null,
"_host": null,
"_readableState": {
"objectMode": false,
"highWaterMark": 0,
"buffer": {
"head": null,
"tail": null,
"length": 0
},
"length": 0,
"pipes": null,
"pipesCount": 0,
"flowing": null,
"ended": false,
"endEmitted": false,
"reading": false,
"sync": false,
"needReadable": true,
"emittedReadable": false,
"readableListening": false,
"resumeScheduled": false,
"defaultEncoding": "utf8",
"ranOut": false,
"awaitDrain": 0,
"readingMore": false,
"decoder": null,
"encoding": null
},
"readable": true,
"domain": null,
"_events": {},
"_eventsCount": 4,
"_writableState": {
"objectMode": false,
"highWaterMark": 0,
"needDrain": false,
"ending": false,
"ended": false,
"finished": false,
"decodeStrings": false,
"defaultEncoding": "utf8",
"length": 0,
"writing": false,
"corked": 0,
"sync": true,
"bufferProcessing": false,
"writecb": null,
"writelen": 0,
"bufferedRequest": null,
"lastBufferedRequest": null,
"pendingcb": 0,
"prefinished": false,
"errorEmitted": false,
"bufferedRequestCount": 0,
"corkedRequestsFree": {
"next": null,
"entry": null
}
},
"writable": false,
"allowHalfOpen": false,
"destroyed": false,
"_bytesDispatched": 0,
"_sockname": null,
"_writev": null,
"_pendingData": null,
"_pendingEncoding": "",
"server": null,
"_server": null,
"isRaw": false,
"isTTY": true,
"fd": 0
},
"argv0": "node",
"mainModule": {
"id": ".",
"exports": {},
"parent": null,
"filename": "/home/froth/freelancer-projects/thistest.js",
"loaded": false,
"children": [
{
"id": "/home/froth/freelancer-projects/node_modules/circular-json/build/circular-json.node.js",
"exports": {},
"parent": "~process~mainModule",
"filename": "/home/froth/freelancer-projects/node_modules/circular-json/build/circular-json.node.js",
"loaded": true,
"children": [],
"paths": [
"/home/froth/freelancer-projects/node_modules/circular-json/build/node_modules",
"/home/froth/freelancer-projects/node_modules/circular-json/node_modules",
"/home/froth/freelancer-projects/node_modules",
"/home/froth/node_modules",
"/home/node_modules",
"/node_modules"
]
}
],
"paths": [
"/home/froth/freelancer-projects/node_modules",
"/home/froth/node_modules",
"/home/node_modules",
"/node_modules"
]
}
},
"console": {}
}
3) undefined
In 3)
this is undefined as it is not autoboxed to an object in strict mode. That means that there is no root object in this context. If you do not use strict mode then your code is boxed by a parent scope.As you can see in the output, within nodejs there is lot of information about node internal stuff.
In 1)
the output is an empty object because in the top-level code in a node module, this is the equivalent to module.exports and module.exports is empty in this example.
While in browsers the global scope is the window
object, in nodeJS the global scope of a module is the module itself, so when you define a variable in the global scope of your nodeJS module, it will be local to this module.
You can read more about it in the NodeJS documentation where it says:
global
<Object> The global namespace object.
In browsers, the top-level scope is the global scope. That means that in browsers if you're in the global scope var something will define a global variable. In Node.js this is different. The top-level scope is not the global scope; var something inside an Node.js module will be local to that module.
And in your code when you write:
console.log(this)
in an empty js file(module) it will print an empty object {}
referring to your empty module.console.log(this);
inside a self invoking function, this
will point to the global nodeJS scope object which contains all NodeJS common properties and methods such as require()
, module
, exports
, console
...console.log(this)
with strict mode inside a self invoking function it will print undefined
as a self invoked function doesn't have a default local scope object in Strict mode.this
in a node module:this
in NodeJS global scope is the current module.exports object, not the global object. This is different from a browser where the global scope is the global window
object. Consider the following code executed in Node:
console.log(this); // logs {}
module.exports.foo = 5;
console.log(this); // log { foo:5 }
First we log an empty object because there are no values in module.exports
in this module. Then we put foo
on the module.exports
object, when we then again log this
we can see that it now logs the updated module.exports
object.
global
object:We can access the global
object in node using the global
keyword:
console.log(global);
The global
object exposes a variety of useful properties about the environment. Also this is the place where functions as setImmediate
and clearTimeout
are located.
I've written a comprehensive answer that covers the value of this
in different contexts. Explanation is added as code comments where needed.
let this_in_objects = {
propA: "let's figure THIS out!",
/*
* Object property set to a standard function.
* */
propB: function () {
return this.propA;
// Returns the value of this_in_objects.propA as expected.
},
/*
* Object property set to an arrow function (Introduced in ES6).
* */
propC: () => {
return this.propA;
// Should return 'undefined'
// In this case, 'this' refers to the surrounding scope, which could be one of the following :
// - 'module.exports' if the code is inside a nodejs module.
// - 'window' if the code is executed in a browser, or 'undefined' if running in a terminal due to the lack of 'window' global variable.
},
/*
* Object property set to a standard function that returns an arrow function.
* */
propD: function () {
let newArrowFunction = () => {
return this.propA;
// Returns the value of this_in_objects.propA.
// The first functions declaration binds 'this' to the current object
// then the second function scope is now the 'this' of the first function.
}
return newArrowFunction;
},
/*
* Object property set another object with 2 properties, one of which returns a standard function.
* */
propE: {
propE_1: "value of propE.propE_1",
propE_2: function () {
return this.propE_1;
// In this case, 'this' refers to the surrounding scope, which is the parent object 'propE'
}
},
/*
* Object property set another object with 2 properties, one of which returns an arrow function.
* */
propF: {
propF_1: "value of propF.propF_1",
propF_2: () => {
return this.propF_1;
// Should return 'undefined'
// There no change in the binding of 'this', so
// In this case, 'this' refers to the surrounding scope, which could be one of the following :
// - 'module.exports' if the code is inside a nodejs module.
// - 'window' if the code is executed in a browser, or 'undefined' if running in a terminal due to the lack of 'window' global variable.
}
},
};
console.log(this_in_objects.propB());
// Returns "let's figure THIS out!"
console.log(this_in_objects.propC());
// Returns 'undefined'
console.log(this_in_objects.propD()());
// Returns "let's figure THIS out!"
// Notice the double round brackets to call the nested anonymous functions.
console.log(this_in_objects.propE.propE_2());
// Returns "value of propE.propE_1"
console.log(this_in_objects.propF.propF_2());
// Returns 'undefined'
this_in_objects.propX = function () {
return this.propA;
// Returns the value of this_in_objects.propA as expected.
};
this_in_objects.propA = 'The new value of propA !';
console.log(this_in_objects.propX());
// Returns "The new value of propA !",
// even though 'propA' value was changed AFTER declaring the function,
// returning the value of 'propA' at the time of function execution, not at the time of declaration.
This is based on information found on:
I hope this helps and please let me know in the comments if I missed any scenarios where the value of this
might be different.
To start with documentation on Global context in node environment
In browsers, the top-level scope is the global scope. That means that in browsers if you're in the global scope var something will define a global variable. In Node.js this is different. The top-level scope is not the global scope; var something inside an Node.js module will be local to that module.
Each JS file is treated as a module. Node automatically wraps the code of a JS file in a self IIFE with exports, require, module, __filename, __dirname
as parameters to the function.
Below is the screenshot of execution context using node-debug
If you run the below code, prints true
which means this
refers to exports
in node.js. Best explained in this answer.
console.log(this === exports);
Which means at execution the code is wrapped something similar as below in Node.js separating your code from global context using a wrapper function context.
var context = (function (exports, require, module, __filename, __dirname) {
console.log(this) //This is my code
});
var module = {exports:{}};
context.apply(module.exports, [module.exports, require, module, "FILE_NAME", "DIR_NAME"]);
Answer to the next point refer this documentation completely:
A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.
so when you execute this code
(function(){
console.log(this);
}());
prints the global
object and in use strict
mode prints undefined
Remember:
In Browser the function is not wrapped by IIFE/wrapper function context as in node, its directly executed on window
object. Hence the calling context varies for Node.js and Browser.
Also read this.