'Importing data via a `require` statement seems to "memorize" the previous value imported by that statement

I have this in file data.js:

module.exports = {
    data: {
        value: 42
    }
};

I am importing that in file test.js:

function load(fileName) {
    const {data} = require(fileName);
    data.value += 5;
    return data;
}

for (let i = 0; i < 5; i++)
    console.log(load("data.js"));

When I execute node test.js, the printout is:

{ value: 47 }
{ value: 52 }
{ value: 57 }
{ value: 62 }
{ value: 67 }

I expected { value: 42 } to be printed in every iteration, but I understand that probably data references the object returned from require(fileName), so changing data.value impacts the contents of that object.

But what I don't understand is, why the require statement isn't executed "from scratch", i.e., on the original input file.

Of course, I can work around this by changing data.js into a properly formatted JSON file, and then load that file using something like:

const data = JSON.parse(fs.readFileAsync(data.json));

But I would like to get a deeper understanding of why using a require statement is inappropriate in this case.

Thanks!



Solution 1:[1]

I think this happens because of the cache feature of the require() function. The require() function caches module and every next time returns the cached version. Each module is only loaded and evaluated the first time it is required since any subsequent call of require() will simply return the cached version. This should be clear by looking at follows:

//D:\\backend node\\backend-projects\\test\\test2.js file
function load(fileName) {
    console.log(`cache 
    ${JSON.stringify(require
     .cache["D:\\backend node\\backend-projects\\test\\test2.js"]
    .children)}`) //here I print the children property of the require.cache object
    const {data} = require(fileName);
   
    data.value += 5;
    return data;
}

for (let i = 0; i < 5; i++)

    console.log(load("./test.js"));

For simulation here, I use a simple approach, every time when load function invokes, it prints cached data for me so I can see what happens when require function invokes, so this operation returns the following result:

cache
    [] // there is no cached data 
//because require function is invoked first time, it returns original data
{ value: 47 }
cache 
    [{"id":"D:\\backend node\\backend-projects\\test\\test.js","path":"D:\\backend node\\backend-projects\\test","exports":{"data":{"value":47}},"filename":"D:\\backend node\\backend-projects\\test\\test.js","loaded":true,"children":[],"paths":["D:\\backend node\\backend-projects\\test\\node_modules","D:\\backend node\\backend-projects\\node_modules","D:\\backend node\\node_modules","D:\\node_modules"]}]
{ value: 52 }
cache
    [{...,"exports":{"data":{"value":52}},..]}]
{ value: 57 }
cache
    [{...,"exports":{"data":{"value":57}},..]}]
{ value: 62 }
cache
     [{...,"exports":{"data":{"value":62}},..]}]
{ value: 67 }

you can see now, that every time the require function returns the cached version of the module. If it returns the original module, it had to be the original value, not the increased value.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1