简体   繁体   中英

How do I check if a script is running as a ES6 module (so that it can `export`), in Javascript?

I'd like to make a Javascript file that

  • export s its content (eg a class) if it can export (eg it has been loaded with <script type="module"> )
  • and otherwise, assigns its content into the global variable such as window and global .

For example, let's assume such a file print.js .

Case A

One can use it like:

<script type="module">
    import print_things from "./print.js";
    print_things("Javascript innovation");
</script>

Case B

or,

<script src="./print.js"></script>
<script>
    print_things("Hmmmmmmm.");
</script>

Currently, using export makes the script throw an error in Case B : Uncaught SyntaxError: Unexpected token export . So it has to know whether export is available on the environment whereon it runs, in order to support the both use cases. How do I do this?

Check out UMD (universal module definition). Namely, this example

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['exports', 'b'], function (exports, b) {
            factory((root.commonJsStrictGlobal = exports), b);
        });
    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
        // CommonJS
        factory(exports, require('b'));
    } else {
        // Browser globals
        factory((root.commonJsStrictGlobal = {}), root.b);
    }
}(typeof self !== 'undefined' ? self : this, function (exports, b) {
    // Use b in some fashion.

    // attach properties to the exports object to define
    // the exported module properties.
    exports.action = function () {};
}));

Browsers that understand type=module should ignore scripts with a nomodule attribute. This means you can serve a module tree to module-supporting browsers while providing a fall-back to other browsers.

<script type="module" src="module.js"></script>
<script nomodule src="fallback.js"></script>

( Note: you probably shouldn't use this in the real world , but it is totally valid and does exactly what you want.)
Here's an implementation of your print.js :

function print_things(msg) {
   console.log(msg)
}
if(0)typeof await/2//2; export default print_things

 <script type=module> // (renamed to avoid name collision) import print_things2 from "https://12Me21.github.io/external/print.js" print_things2("Javascript innovation") </script> <script src="https://12Me21.github.io/external/print.js"></script> <script> print_things("Hmmmmmmm.") </script>
This syntax will "hide" the export statement from non-module scripts, because await/2//2; ... await/2//2; ... is parsed differently depending on context:

  • In a module or async function :
    await (operator) /2/ (regexp) / (divide) 2 (number)
  • In a normal script :
    await (variable) / (divide) 2 (number) //2 ... ( comment )

When it's parsed as a comment, the rest of the line is ignored. So, the export statement is only visible to module scripts.

Here is the relevant part of the specification: 15.8 Async Function Definitions > Syntax > AwaitExpression > Note 1

Compatibility

As a non-module script, it should work in any browser (even ancient versions of Internet Explorer etc.), and is still valid with "use strict" enabled

However, loading it as a module requires support for "top-level await", which was added a few years after modules themselves (~2021 vs ~2018), so keep that in mind.
(It also works with nodejs, in either mode)

I've used this trick in a library that I wrote, and it's been working fine in multiple environments for several months. However, it has caused some problems with external tools (frameworks, compilers, minifiers, etc.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM