【问题标题】:Dynamic ES6 module scopes动态 ES6 模块范围
【发布时间】:2015-08-06 05:25:57
【问题描述】:

所有当前的模块加载器,如AMD,CommonJS,SystemJS,使用变量定义将外部对象加载到当前模块范围

喜欢:

var something = require('something');

或:

define('module',['something'],function(something){});

如果您不知道需要从外部模块导入什么,或者只需要导入所有内容,这将成为问题,因为无法在运行时定义变量。

我想这是 ES6 翻译器不使用的主要原因

import * from 'something';

语法,它们不包含在 ES6 规范中。

所以说动态模块范围我的意思是模块变量可以在运行时定义/加载,这将允许将 ES6 语法扩展到某些东西 喜欢:

import * from 'something';
import Something.* from 'something';
import /regexp/ from 'something';

在我看来,这是定义导入的更佳方式,而不是列出所有名称,例如:

import {
    ONE,
    TWO,
    ...
} from 'something';

现在我真正的问题是:

为什么不使用with 来实现呢?

这里是从 ES6 到 ES5 的简单示例翻译,可以解决问题:

文件:modules/module-1.js

var localVar = 'VALUE';
function localFun(a,b){
    return a +' and '+ b;
}
export {
    localVar as module1Var
    localFun as module1Fun
}

到:

(function(){
    // define module named 'modules/module-1' 
    // and return related scope object 
    // containing 'execute_module' function predefined 
    with (define_module('modules/module-1')) {
        // will register actual module execution 
        // which will be called after all imports 
        // initialized 
        execute_module(function(){
            var localVar = 'VALUE';
            function localFun(a,b){
                return a +' and '+ b;
            }
            // returns value as current module exports 
            return {
                module1Var : localVar,
                module1Fun : localFun
                // ...
            };
        });
    }
})();

文件:modules/module-1.js

import * from 'modules/module-1.js';

console.info(module1Fun(module1Var)); //prints: VALUE and VALUE

到:

(function(){
    // define module named 'modules/module-2' 
    // after execute is called loader will do 
    // all imports and bind exports from modules 
    // to current module scope and then invoke callback
    // after which imported variables will appear in with scope
    // and will be visible in callback scope. 
    with (define_module('modules/module-2',{
        'modules/module-1' : '*',
        // also can filter exports by regexp
        'modules/other'    : /.*/
    })) {
        execute_module(function(){
            console.info(module1Fun(module1Var)); //prints: VALUE and VALUE                
        });
    }
})();

真的有必要避免with,即使在转译器/加载器中?

感谢您对这些人的看法,因为我正在考虑编写另一个 ES6to5 翻译器和模块加载器。 :)

【问题讨论】:

  • 为什么要写一个不符合规范的转译器?这对我来说似乎没有意义。当然你可以为所欲为,使用你自己的 JS 方言。

标签: javascript ecmascript-6 babeljs traceur es6-module-loader


【解决方案1】:

所以有几个很好的理由不这样做......

...首先,在 JS 中使用全局或“全局”变量来涂抹工作上下文是一种不好的做法,而不知道这些变量是什么。

如果该导入的内容发生变化,并且我使用的是 with(这不是真正有效的 ES5... ...ES5 是严格模式子集,则留下 with 以允许 ES3代码在 ES5 浏览器中运行而不会爆炸),那么我要担心的不仅仅是尝试在已更改的模块上调用方法...

// some-module/v1.0/some-module.js
// ...
export { doA, doB, a, b }

// my-app/index.js
import * with "./some-module/v1.0/some-module";

const c = 1;
let doC = ( ) => doA( a ) + doB( b );

看起来不错,当然。一点伤害都没有。

如果some-module 是一个 NPM 包,我处于开发模式怎么办,所以我想在功能添加中不断更新some-module,因为我知道某些功能会让我的生活更轻松,当他们土地:

// some-module/v1.5/some-module.js
export { doA, doB, doC, a, b, c };

// my-app/index.js
import * with "./some-module/v1.5/some-module";

const c = 1;
/*
  ...
*/
let doC = ( ) => doA( a ) + doB( b );

轰隆隆!

c is already defined

如果您搜索所有已编译的导入库/模块/组件(假设您在应用程序的入口点中有 8 个)... ...您最终找到将 c 作为全局导入的源,然后您更新所有您的代码,将您的c 的名称替换为其他名称,然后替换对它的所有引用,这样它们就不会再爆炸了,接下来会发生什么?

轰隆隆! doC is already defined

重复该过程并找到有问题的文件。

我们如何解决这些问题?
通常,这是通过命名空间。

我们已经在那些import 语句中命名空间,这些语句大部分都可以正常工作(在 ES7 中进一步简化了进一步的建议)。

import someModule from "./some-module/v1.0/some-module";

let doC = ( ) => someModule.doA( someModule.a ) + someModule.doB( someModule.b );

突然之间,您可以清楚地看到在您的应用中的任何地方没有担心方法/值冲突,除非这是您自己的错。

此外,如果您加载整个库并决定通过直接引用这些值/方法中的一些来节省时间,您可以选择这样做。

import someModule from "./some-module/v1.5/some-module";
const { a, b } = someModule;
let { doA, doB } = someModule;

const c = 1;
let doC = ( ) => doA( a ) + doB( b ) + someModule.doC( c );

仍然存在 0% 的歧义,但由于让消费者可以选择如何处理导入,所有代码都已尽可能小。

【讨论】:

  • 抱歉没有找到对你的答案发表评论的方法,所以我已经作为对你答案的评论回答了:)
  • 加一个用于使用单词“slather”。
【解决方案2】:

@norguard,“严格模式”和模块更新很好,但是:

严格模式在实际执行回调中仍然可以使用

booms !本地执行范围的定义会覆盖(隐藏)当前上下文中with范围的定义,所以不存在重新定义的问题。

// some-module/v1.5/some-module.js
export { doA, doB, doC, a, b, c };
// my-app/index.js
import * with "./some-module/v1.5/some-module";
const c = 1;
/*
  ...
*/
let doC = ( ) => doA( a ) + doB( b );

// translated to:
(function(){
    // with scope contain { doA, doB, doC, a, b, c }
    with (define_module('my-app/index.js',{
        'some-module/v1.5/some-module.js':'*'
    })) {
        execute_module(function(){
            "use struct"

            // local var 'c' overrides definition 'c' of 'with scope'
            // in current function context.
            var c = 1; 
            //  same for 'doC' function 
            var doC = function( ){return doA( a ) + doB( b )};

        });
    }
})();

所以上面的一切仍然有效。

【讨论】:

  • 我真的不确定您希望从中得到什么。首先:规范期望import myModule from "my-module"; 等于var myModule = require("my-module").default;,就目前而言。该变量定义在模块的根范围内定义在模块的根范围内(使用@987654324实际上是无效的@/export 声明在您正在处理的文件的顶层以外的任何范围内。另外,就像我提到的,如果您有 8 个文件,您将 withing 到您的“index.js”中,每个其中有 5-10 个 IT 是 withing,可能通过...
  • ...你如何解决这个问题?我所依赖的模块现在是否是 JS 规范的一部分?如果我导入“1.js”、“2.js”和“3.js”,我的全局变量应该不同于“3.js”“2.js”“1.js”吗?最后是“时间死区”。我不能使用letconst 来定义已经在当前范围内初始化的东西。此外,我不能在它被引用的地方引用 above 的值,包括阴影。因此,假设您符合整个规范,这会中断。不合规很好……但这是你的特权……
猜你喜欢
  • 2023-03-20
  • 1970-01-01
  • 2016-06-06
  • 1970-01-01
  • 1970-01-01
  • 2011-05-13
  • 1970-01-01
  • 2017-07-23
  • 1970-01-01
相关资源
最近更新 更多