Изменить переменную область действия в Javascript

Меня не интересует вызов или применяется для изменения ссылки this. Только для моих собственных интересов я играю с идеей для другого, требует техники для javascript, которая сделала бы для некоторых более чистых определений, и цель заключалась в том, чтобы не передавать массивы или дублировать имена ссылочных модулей для моих определений.

У меня есть примерное решение (просто доказательство концепции), использующее toString и eval для функции, но мне интересно, есть ли более безопасный или более эффективный способ сделать это.

// Sample module libraries (would probably be in their own files)
someModules = { 
    testModule: {test: function(){console.log("test from someModule")}},
    anotherModule: { doStuff: function(){console.log("Doin stuffs!");}}
};

sampleRequire = function() {

    // Load the modules
    for (var i=arguments.length-2; i>=0; --i){

        // Create a local variable reference to the module
        eval ('var '+arguments[i]+' = someModules.'+arguments[i].toString());
    }

    // Redefine the programmer function so that it has my local vars in its scope
    eval("var fn = "+arguments[arguments.length-1]);

    return fn;
}

// Main code...
sampleRequire( 'testModule', 'anotherModule',
    function(){ 
        testModule.test();
        anotherModule.doStuff();
    }
)();

Edit:

Pointy сделал отличную оценку, что это полностью разрушит масштаб основной функции, что часто бывает неприемлемым. В идеале я хотел бы видеть, что переменные модуля добавляются в область функций, не сбивая другие переменные с областью (за исключением имен модулей - программист должен знать, что нельзя использовать одно и то же имя для двух вещей). Я уверен, что это, вероятно, невозможно, но я все равно буду любить некоторые идеи.

Другая цель - сделать это гибко, не добавляя аргументы на модуль к основной функции. В противном случае мы вернемся к квадрату с стилями CommonJS (которые я не пытаюсь сражаться, просто интересуюсь областью!).

Ответы

Ответ 1

Я склонен говорить, что "вы делаете это неправильно". Использование необъявленных переменных никогда не является хорошей идеей, даже если вы можете.

Вот еще один хак, который записывает модули в глобальный объект. Однако это может иметь побочные эффекты для методов, вызванных основной функцией.

sampleRequire = function() {

    var cache = {};
    var moduleNames = [].slice.call(arguments);
    var fn = moduleNames.pop();

    return function () {
        var result, name, i;
        // export modules to global object
        for (i = 0; i < moduleNames.length; i++) {
            name = moduleNames[i];
            cache[name] = window[name]; // remember old values
            window[name] = someModules[name];
        }
        result = fn.apply(null, arguments);
        // restore original global stuff
        for (i = 0; i < moduleNames.length; i++) {
            name = moduleNames[i];
            window[name] = cache[name];
        }
        return result;
    };
}

Я также пробовал какую-то магию с ключевым словом with, которое в основном было сделано именно для того, что вы хотите. Однако, похоже, что в этом случае он не работает без eval.

Ответ 2

Я не могу думать о другом способе делать то, что тебе нужно. Я также подозреваю, что это может быть одним из немногих вариантов использования eval, который не является злом. Но имейте в виду, что модули могут полагаться на свои области, и это может сломать их.

Ответ 3

Как насчет чего-то типа:

someModules = { 
  testModule: {test: function(){console.log("test from someModule")}},
  anotherModule: { doStuff: function(){console.log("Doin stuffs!");}}
};

function requireModules() {
  var all = [];
  for (var i = 0, l = arguments.length; i<l; i++) {
    all.push(someModules[i]);
  }
  return all;
}

(function(testModule,anotherModule){
  testModule.test();
  anotherModule.doStuff();
}).apply(null,requireModules('testModule','anotherModule'));