• javascript模块化编程的cmd规范(sea.js)


    CMD(Common Module Definition,通用模块定义)是一种模块定义规范,规范中明确了模块的基本书写格式和基本交互规则。SeaJS就是遵循的这个规范。

    define函数

    在CMD规范中,一个模块就是一个文件,模块的区分通过define关键字来定义,最基本的格式是:

    define(factory); // 这里的define是一个全局函数,factory可以是函数、对象或字符串

    define接受factory参数,factory可以是一个函数,也可以是一个对象或字符串。

    当factory为对象、字符串时,表示模块的接口就是该对象、字符串。

    比如可以如下定义一个JSON数据模块:

    define({ "foo": "bar" });

    也可以通过字符串定义模板模块:

    define('I am a template. My name is {{name}}.');

    当factory为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。

    当factory方法在执行时,默认会传入三个参数:require、exports和module:

    define(function(require, exports, module) {
        // 模块代码
    });

    define也可以接受两个以上的参数,进阶的格式是:

    define(id, deps, factory); // id是模块标识字符串,deps是模块依赖数组

    参数中,字符串id表示模块标注,数组deps表示模块依赖。

    define('helloworld', ['jquery'], function(require, exports, module) {
        // 模块代码
    });

    id和deps参数是可以省略的,省略时可以通过构建工具自动生成。注意的是带id和deps参数的define用法不属于CMD规范,而属于Modules/Transport规范。

    define函数带有成员属性cmd,是一个空对象,可用来判断当前页面是否有CMD模块加载器。

    if (typeof define === "function" && define.cmd) {
        // 有 Sea.js 等 CMD 模块加载器存在时的逻辑代码
    }

    require函数(factory函数的参数之一)

    require是factory函数的第一个参数,它有下列的用法:

    require(id),通过接受模块标识作为唯一参数,用来获取其他模块提供的接口。

    define(function(require, exports) {
        // 获取模块 a 的接口
        var a = require('./helloworlda');
    
        // 调用模块 helloworld  的方法
        a.doSomething();
    });

    require.async(id, callback),用来在模块内部加载模块,并在加载完成后执行指定回调。callback参数可选。

    define(function(require, exports, module) {
        // 异步加载一个模块,在加载完成时,执行回调
        require.async('./b', function(b) {
            b.doSomething();
        });
    
        // 异步加载多个模块,在加载完成时,执行回调
        require.async(['./c', './d'], function(c, d) {
            c.doSomething();
            d.doSomething();
        });
    });

    与上一个用法不同的是,前者是同步往下执行,后者是异步回调执行,一般用来加载可延迟异步加载的模块。

    require.resolve(id),使用模块系统内部的路径解析机制来解析并返回模块路径,该函数并不会加载模块,只返回解析后的绝对路径。

    define(function(require, exports) {
        console.log(require.resolve('./b'));// http://path/b.js
    });

    这样就可以获取模块的路径,一般用在插件环境或需要动态拼接模块路径的场景下。

    exports对象(factory函数的参数之一)

    exports是一个对象,用来向外提供模块接口。

    define(function(require, exports) {
        // 对外提供 foo 属性
        exports.foo = 'bar';
    
        // 对外提供 doSomething 方法
        exports.doSomething = function() {};
    });

    除了给exports对象增加成员,还可以使用return直接向外提供接口。

    define(function(require) {
        // 通过 return 直接提供接口
        return {
            foo: 'bar',
            doSomething: function() {}
        };
    });

    如果return语句是模块中的唯一代码,还可以简化为(factory为对象或字符串):

    define({
        foo: 'bar',
        doSomething: function() {}
    });

    上面这种格式特别适合定义一个JSONP模块。

    要注意的是,exports仅仅是module.exports的一个引用,在factory内部给exports重新赋值时,并不会改变module.exports的值,因此给exports赋值是无效的,不能用来更改模块接口。比如以下的写法就是错误的:

    define(function(require, exports) {
        // 错误用法!!!
        exports = { // 应该改为module.exports = {}
            foo: 'bar',
            doSomething: function() {}
        };
    });

    module对象(factory函数的参数之一)

    module是一个对象,存储与当前模块相关的一些属性和方法。

    module.id,模块的唯一标识。

    define('yanggb', [], function(require, exports, module) {
        // 模块代码
        console.log(module.id); // yanggb
    });

    define函数的第一个参数就是模块唯一标识,用module取得时候就能得到yanggb。

    module.uri,模块的绝对路径,根据模块系统的路径解析规则得到。

    define(function(require, exports, module) {
        // 模块代码
        console.log(module.uri); // http://path/file.js
    });

    一般情况下(没有在define函数中自定义id参数),module.id的值就是module.uri的值。

    module.dependencies,模块的依赖,是一个数组。

    module.exports,模块对外提供的接口。

    传给factory函数的exports参数是module.exports对象的一个引用。只通过exports参数来提供接口,有时无法满足开发者的所有需求。例如当模块的接口是某个类的实例时,就需要通过module.exports来实现:

    define(function(require, exports, module) {
        // exports 是 module.exports 的一个引用
        console.log(module.exports === exports); // true
    
        // 重新给 module.exports 赋值
        module.exports = new SomeClass();
    
        // exports 不再等于 module.exports
        console.log(module.exports === exports); // false
    });

    要注意的是,对module.exports的赋值必须要同步执行,不能放到回调函数里(异步)。

    // x.js
    define(function(require, exports, module) {
        // 错误用法
        setTimeout(function() {
            module.exports = {a: "hello"};
        }, 0);
    });
    
    // y.js,在 y.js 里有调用到上面的 x.js:
    define(function(require, exports, module) {
        var x = require('./x');
    
        // 无法立刻得到模块 x 的属性 a (有延迟)
        console.log(x.a); // undefined
    });

    总结

    这里学习的顺序就是:define函数 -> factory函数(define函数的参数) -> require函数、exports对象和modules对象(factory函数的参数)。其实用法也比较简单,对开发很有帮助,有助于开发思路的整理和后期代码的维护。优点也就是模块化编程的优点。

    "我唱的不够动人你别皱眉。"

  • 相关阅读:
    访问控制与封装
    构造函数
    定义抽象数据类型
    函数基础
    参数传递
    路径中 斜杠/和反斜杠 的区别
    【git】Github上面的开源代码怎么在本地编译运行
    IDEA及IDEA汉化包
    Java设计模式——工厂设计模式
    "/"程序中的服务器错误
  • 原文地址:https://www.cnblogs.com/yanggb/p/10796839.html
Copyright © 2020-2023  润新知