0

I have two module settingmap.js

var settingMap = {
    scWidth : [4000, 6000, 8000],
    scHeight : [5000, 7000, 9000],
    bxWidth : [100, 90, 80],
    bxHeight : [100, 90, 80],
    totalTime : [50, 40, 30],
    level : [1, 2, 3],
    boxColor : ['yellow', 'green', 'blue']
};

and setting.js

define(['settingmap', 'gamestatus'], function (settingMap, gamestatus) {

    var setting = {
    scWidth : settingMap.scWidth[gamestatus.levelIndex],
    scHeight : settingMap.scHeight[gamestatus.levelIndex],
    bxWidth : settingMap.bxWidth[gamestatus.levelIndex],
    bxHeight : settingMap.bxHeight[gamestatus.levelIndex],
    totalTime : settingMap.totalTime[gamestatus.levelIndex],
    level : settingMap.level[gamestatus.levelIndex],
    maxLevel : settingMap.level.length,
    boxColor : settingMap.boxColor[gamestatus.levelIndex]
    };

    return setting;

});

and I use the setting in some other module,

I will make gamestatus.levelIndex++

but I always got the old setting without gamestatus.levelIndex++.

How to refresh module setting?

chanjianyi
  • 607
  • 4
  • 15
  • 35

2 Answers2

1

Define scWidth as a getter:

var setting = {
    get scWidth () { 
        return settingMap.scWidth[gamestatus.levelIndex]; 
    }
};

This way gamestatus.levelIndex will be evaluated each time you want to get the value of scWidth.

In your current code, scWidth is set to a value once and won't change unless you have other code that assigns directly to it.

In theory you could "refresh" a RequireJS module but I do not advocate using this approach for the goal you have. It is complicated to get it right.


You've edited your question to add a whole bunch of other values in settings that map to values in settingMap. Fair enough, here's how I'd do it. Define in settings all the fields that do not follow the pattern of settings.<name> -> settingMap.<name>[gamestatus.levelIndex] and then run a loop to define getters for all those fields that do follow the pattern:

// Extend this list to all the fields that follow the pattern.
var fields = ["scWidth", "scHeight"];

for (var i = 0, field; (field = fields[i]); ++i) {
    (function (field) {
        Object.defineProperty(settings, field, {
            get: function () {
                return settingMap[field][gamestatus.levelIndex];
            }
        });
    })(field);
}

Here's a complete piece of code that shows it works:

var gamestatus = {
    levelIndex: 0
};

var settingMap = {
    "scWidth": [ "scWidth0", "scWidth1"],
    "scHeight": [ "scHeight0", "scHeight1"]
};

var settings = {
    // Any field that does not follow the pattern can be put here.
    maxLevel : settingMap.level.length
};

// Extend this list to all the fields that follow the pattern.
var fields = ["scWidth", "scHeight"];

for (var i = 0, field; (field = fields[i]); ++i) {
    (function (field) {
        Object.defineProperty(settings, field, {
            get: function () {
                return settingMap[field][gamestatus.levelIndex];
            }
        });
    })(field);
}

console.log(settings.scWidth);
console.log(settings.scHeight);
gamestatus.levelIndex++;
console.log(settings.scWidth);
console.log(settings.scHeight);

Notes on the code:

  1. Getters and how do define them is well documented here.

  2. The anonymous function in the loop that is called right away is to avoid the problem of closures in loops. (See this question for a complete discussion of what the problem is.)

  3. The code of the loop:

    for (var i = 0, field; (field = fields[i]); ++i) {
    

    could be:

    for (var i = 0; i < fields.length; ++i) {
        var field = fields[i];
    

    The two ways to do it are functionally equivalent so long as fields does not contain a value that can evaluate to false (which is the case here).

Community
  • 1
  • 1
Louis
  • 146,715
  • 28
  • 274
  • 320
  • Thank you very much. it works! and very detail answer! Althought I'm not understand the code yet XD. I will try to understand it later.. and how to add `maxLevel` field in it? – chanjianyi Sep 14 '15 at 17:03
  • See the latest edits. Specifically regarding `maxLevel`: look at how I've defined it in the "complete" piece of code. (The longest one.) – Louis Sep 14 '15 at 17:12
0

Here is how you refresh a node module (remove it from the cache) so that it will be required fresh from the file system on the next call to require.

Use require.resolve to get ahold of the absolute path for the module loaded by require. For example, let's say you required your module like this:

var settings = require('./settings.js');

Then you can delete that module from the require cache like this:

// Use require.resolve to get the full path of the module.
var modulePath = require.resolve('./settings.js');
// The full path is the key of the cached module on require.cache.
delete require.cache[modulePath];

Now your module is removed from the cache. Keep in mind that if the module you required also required other modules itself, they will still be cached. Here's what the cache looks like when I require a custom module I wrote that doesn't require any other modules.

Here's what the cache looks like after simply requiring the bluebird module from npm:

As you can see, just requiring bluebird loaded in a bunch of modules to the cache. This isn't a problem in many cases, but it might be if the behavior you were trying to reset is actually deeper in the dependency tree, then re-requiring bluebird fresh might re-run the code for bluebird, but when bluebird requires any of those other modules again, they will be returned from the module cache.

If you need to wipe out a dependency and its entire tree, iterate over the cache and use a regular expression.

Object.keys(require.cache).forEach(function (key) {
  if (key.test(/.*\/projectName\/node\_modules\/moduleName\/.*$/)) {
    delete require.cache[key];
  }
});

That looks in the require cache for any modules in your project's node_modules directory under the module you're trying to uncache. In the above example it would remove the bluebird module from the cache as well as any other modules cached under bluebird.


NOTE: I agree with Louis that the fact that you need to do this is a sign that you probably need to go about things differently.

CatDadCode
  • 58,507
  • 61
  • 212
  • 318
  • 1
    Your answer works so long as the only thing in use to load modules is Node's `require`, but the OP is using RequireJS. The code may be running in Node (which is quite doable) but if the OP wants to reload a module loaded with RequireJS the OP then must deal with RequireJS' quirks, which are different from Node's. I've dealt with that [there](http://stackoverflow.com/a/19967014/1906307) (not the difference, just RequireJS's quirks). – Louis Sep 14 '15 at 17:21
  • Missed that bit. My apologies. – CatDadCode Sep 14 '15 at 17:40