Reputation: 443
I'm trying to process some json and write pieces of data to a few files.
So I have a function to create folder/files based on the json data and some literals. The filenames are defined by an object containing different categories:
const folders = {
category1: {
fileName: 'default',
path : '/path',
subpath : () => `/subpath/${this.fileName}${this.uniqueId}`
}
}
This is where half of you will jump ahead and tell me that arrow functions can't see this
of their own objects etc. I know that and that's intentional, since I'll be getting the necessary data later on.
The main function follows this pattern:
function readSave() {
//suppose readFileSync would return a string 'filename' and an int 1029
this.fileName = 'filename';
this.uniqueId = 1029;
let filePath = folders.category1.path + folders.category1.subpath();
// I'd go on to write stuff to that file but for now let's just return
return filePath;
}
readSave()
// returns '/path/subpath/undefinedundefined'
// expected '/path/subpath/filename1029'
I also know that I could just pass fileName
and uniqueId
as args but that's not the point. This post is not exactly an attempt to find a solution but to understand why it's not working.
The confusion here is the use of this
inside readSave
. According to MDN this
inside a regular function is the same object where the function is called. Since I'm calling it on a plain js file on node, it's global
.
All good for now. If I inspect the run inside the function call, this
is global
and the properties are set without problems.
The problem is folders.category1.subpath()
evaluates to undefined
.
The debugger shows when it goes to evaluate () => /subpath/${this.fileName}${this.uniqueId}
, this
is not global
anymore, instead it's an empty Object. This doc makes me think that the arrow function should inherit the this
of the scope it's being called, which is readSave
, which means this
should be global
.
To increase my confusion, setting the properties outside the function works flawlessly:
function readSave2() {
let filePath = folders.category1.path + folders.category1.subpath();
return filePath;
}
this.fileName = 'filename';
this.uniqueId = 1029;
readSave()
// returns '/path/subpath/filename1029'
Inspecting the code above, everything is pretty much the same up until when it goes to evaluate () => /subpath/${this.fileName}${this.uniqueId}
. Now the previously empty Object has two properties as they were set.
Finally, this also works exactly the same as the previous example:
const readSave3 = () => {
this.fileName = 'filename';
this.uniqueId = 1029;
let filePath = folders.category1.path + folders.category1.subpath();
return filePath;
}
I've browsed and read about this for hours but am still really confused as to why some methods work and some don't.
Thanks in advance
Update: Turns out I made some wrong assumptions on what Node's root object is in a given file or function call. Wasn't really aware of the module wrapper.
Upvotes: 3
Views: 106
Reputation: 40842
This doc makes me think that the arrow function should inherit the this of the scope it's being called, which is readSave, which means this should be global.
No the this
of the arrow function is determined at the time the arrow function is created:
[...] No matter what, foo's
this
is set to what it was when it was created (in the example above, the global object). The same applies to arrow functions created inside other functions: theirthis
remains that of the enclosing lexical context [...].
So this
in the arrow function refers to what this
is here:
console.dir(this) // <---- refers to `exports`
const folders = {
category1: {
fileName: 'default'
path : '/path',
subpath : () => `/subpath/${this.fileName}${this.uniqueId}`
}
}
And the this
for those two code block refer to the same object for this exact same reason:
console.dir(this) // the `this` in the arrow function below is the same as here
// and `this` refers to `exports`
const readSave3 = () => {
this.fileName = 'filename';
this.uniqueId = 1029;
// ...
}
readSave3()
function readSave() {
// ...
}
this.fileName = 'filename';
this.uniqueId = 1029;
readSave()
On load the content of a node file is wrapped into: (The module wrapper)
(function(exports, require, module, __filename, __dirname) {
/*... filecontent ... */
})
That function is then called passing the corresponding values as arguments and that function is called on the object passed as exports
to the function What is the root object in Node.js.
So for /subpath/${this.fileName}${this.uniqueId}
the this
refers to exports
the same is with readSave3
and your last code. For your readSave
(the first one) the this
refers to the global object.
So for node your code would look like this:
var moduleSetup = function(exports, require, module, __filename, __dirname) {
// here `this` is exports
const folders = {
category1: {
fileName: 'default',
path: '/path',
// `this`referes to exports due to arrow function
subpath: () => `/subpath/${this.fileName}${this.uniqueId}`
}
}
function readSave1() {
// here `this` refers to `global` because `readSave1` is not called on an object
//suppose readFileSync would return a string 'filename' and an int 1029
this.fileName = 'filename';
this.uniqueId = 1029;
let filePath = folders.category1.path + folders.category1.subpath();
// I'd go on to write stuff to that file but for now let's just return
return filePath;
}
readSave1()
function readSave2() {
let filePath = folders.category1.path + folders.category1.subpath();
return filePath;
}
// `this` refers to `exports`
this.fileName = 'filename';
this.uniqueId = 1029;
readSave2()
const readSave3 = () => {
// `this` refers to `exports` due to arrow function
this.fileName = 'filename';
this.uniqueId = 1029;
let filePath = folders.category1.path + folders.category1.subpath();
return filePath;
}
readSave3()
}
// and that's roughly how node would invoce that function:
var module = {
exports: {}
}
moduleSetup.call(module.exports, // moduleSetup called on module.exports
// with these arguments:
module.exports, require, module, theFileName, theDirname)
Upvotes: 1
Reputation: 21
this
always point the current context.
when the arrow function going to execute this
change to its current context which is in your case is the arrow function. so the this
inside the arrow function and outside of the arrow functions are not same they are in different context this
Upvotes: 0