博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
简单实现 JavaScript 模块加载
阅读量:5867 次
发布时间:2019-06-19

本文共 3583 字,大约阅读时间需要 11 分钟。

JavaScript语言官方未实现命名空间,我们定义一个define函数以实现命名空间。define函数的使用如:

define(function(exports, module, require) {    const $ = require('http://path/to/defined-jquery');    $(function(){        // dom ready!!!    });});

我们可以这样实现:

(function(global) {    'use strict';    var errMsg = Math.random().toString(32).substr(2);    // rootModule 对象保存着所有已加载的模块    var rootModule = {};    // 每一个模块实例都有id属性作为require时查找的标识符,    // exports属性作为对外暴露的对象,loaded属性表示是否加载完。    function ModuleCtor(id) {        if (!this || this.__proto__ !== ModuleCtor.prototype) {            return new ModuleCtor(id);        }        this.id = id;        this.exports = {};        this.loaded = !1;    }    function define(id, fn) {        // 手动赋值模块id,兼容一个script里有多个define。        if (typeof id === 'function') {            fn = id;            id = document.currentScript                ? document.currentScript.src                : Math.random()                      .toString(32)                      .substr(2);        }        if (typeof id !== 'string') {            id = '' + id;        }        var module = ModuleCtor(id);        exec();        function __require__(src) {            // 如果依赖已经加载过直接返回module.exports,            // 如果没有加载过则通过jsonp加载,并且抛出一个异常来打断原函数执行,在子模块加载完毕后重新执行原函数模拟异步代码阻塞同步执行。            if (rootModule[src] && rootModule[src].__proto__ === ModuleCtor.prototype) {                return rootModule[src].exports;            }            loadScript(src, function() {                exec();            });            throw new Error(errMsg);        }        function exec() {            // 将__require__函数传入fn,来支持模块内引用其他模块。            try {                fn.call(module.exports, module.exports, module, __require__);                module.loaded = !0;                rootModule[id] = module;            } catch (err) {                if (err.message !== errMsg) {                    throw err;                }            }        }    }    function loadScript(src, callback) {        var script = document.createElement('script');        script.src = src;        script.onload = function() {            callback && callback(src);        };        document.body.appendChild(script);        return script;    }    // 暴露define给全局    global.define = define;})(window);

这个模块加载的实现有很多不足,如果模块内有很多require时会被执行很多次,所以最好子模块内都是函数不要有自己的状态。seajs的依赖解决方法是,调用函数的toString方法来获得函数字面量,然后在parse出模块依赖,先加载依赖,每一个依赖加载完成后都emit一个事件,当所有依赖都加载完毕后,才执行factory函数,factory函数只执行一次,但是模块加载的顺序和require的顺序基本没有关系(并发请求,谁都有可能先到)。

======= 一本正经的分割线 ======

顺便吐槽一下seajs,由于某种原因,我再8102年见到了seajs而我在3000年前没用过。文档始终没有交代require('caonima');是如何打断函数执行的。看了源码发现是用了Function.prototype.toString方法,然后分析依赖并发加载(require函数是没有顺序之分的)。看源码前,我自己为了模拟该行为,通过抛出异常再反复的重新执行也实现了一个文件加载,而且我这个更贱的货还是真的同步引入依赖,更加cmd一些。

附 Webpack 模块加载原理:

(function(modulesArr) {    var rootModule = {};    function __require__(id) {        if (rootModule[id]) {            return rootModule[id].exports;        }        var currentModule = modulesArr[id];        var module = {            id,            exports: {}        };        currentModule.call(module.exports, module.exports, module, __require__);        currentModule[id] = module;        return module.exports;    }    return __require__(0);})([    function(exports, module, require) {        var m1 = require(1);        console.log(m1);    },    function(exports, module, require) {        exports.msg = 'Hello World';        var m2 = require(2);        m2();    },    function(exports, module, require) {        module.exports = function() {            var str = 'Hello World';            console.log(str);            return str;        };    }]);

转载地址:http://honnx.baihongyu.com/

你可能感兴趣的文章
关于数组做形参,用sizeof测长度的问题
查看>>
C语言单链表(不带头节点)
查看>>
程序包管理RPM、YUM及源代码编译
查看>>
CentOS下部署cobbler
查看>>
Oracle删除当前连接的用户
查看>>
linux学习笔记-文件
查看>>
linux drbd高可用集群
查看>>
python中的对象
查看>>
我的友情链接
查看>>
JMS和ActiveMQ介绍(3)_ActiveMQ
查看>>
压缩/解压PCM数据
查看>>
xen创建硬盘ISO库下载并导入WinXP简版
查看>>
MySQL数据库登录小贴士
查看>>
我的友情链接
查看>>
【java多线程的代价】
查看>>
LNMP—Nginx的编译安装
查看>>
我的友情链接
查看>>
Linux系统中rpm软件包的管理
查看>>
ppp 多点接入
查看>>
svn安装配置文档
查看>>