• JavaScript模块


    在JavaScript编程中我们用的很多的一个场景就是写模块。可以看成一个简单的封装或者是一个类库的开始,有哪些形式呢,先来一个简单的模块。

    简单模块

    复制代码
           var foo = (function() {
                    var name = "foo";
                    function hello() {
                        console.log("hello "+name);
                    }
                    function doWork() {
                        console.log("do work");
                    }
                    return {
                        hello: hello,
                        doWork: doWork
                    };
                })();
                foo.hello(); // hello foo
                foo.doWork(); // do work
    复制代码

    用IIFE创建一个闭包,隔离作用域,避免变量相互干扰。得到foo对象可以直接用了。这种适合小的模块,比如在ag中的写Service。

    复制代码
    (function () {
        angular
            .module('readApp')
            .service('authentication', authentication);
        
        authentication.$inject = ['$window','$http'];
        function authentication($window, $http) {
            var saveToken = function (token) {
                $window.localStorage['read-token'] = token;
            };
            var getToken = function () {
                return $window.localStorage['read-token'];
            };
            var register = function(user) {
                return $http.post('/api/register', user).success(function(data) {
                    saveToken(data.token);
                });
            };
            var login = function(user) {
                return $http.post('/api/login', user).success(function(data) {
                    saveToken(data.token);
                });
            };
            var logout = function() {
                $window.localStorage.removeItem('read-token');
            };
    
            var isLoggedIn = function() {
                var token = getToken();
                if (token) {
                    var payload = JSON.parse($window.atob(token.split('.')[1]));
                    return payload.exp > Date.now() / 1000;
                } else {
                    return false;
                }
            };
            var currentUser = function() {
                if (isLoggedIn()) {
                    var token = getToken();
                    var payload = JSON.parse($window.atob(token.split('.')[1]));
                    return {
                        email: payload.email,
                        name: payload.name,
                    };
                }
            };
            
    
            return {
                saveToken: saveToken,
                getToken: getToken,
                register: register,
                login: login,
                logout: logout,
                isLoggedIn: isLoggedIn,
                currentUser: currentUser,
            };
        }
    })();
    复制代码

    也可以直接一个大括号的:

    复制代码
       var foo = {
                    other: function () {
                        console.log("do other");
                    },
                    hello:function() {
                        console.log("working");
                    },
                    doWork:function() {
                        console.log("working");
                        foo.other();
                    }
                }
    复制代码

    这个形式我们在jquery内部或者一些工具类js中见过。简单直接。如果有内部相互调用,建议直接用对象名。这样不必在每一个方法里面写一个 var self=this 。缺点就是太长了不好维护,多增加一个变量都要加个key:value的形式。只适合简单场景。但如果模块内部方法比较多,还是建议在内部创建对象。

    扩展模块

    复制代码
      var foo = (function () {
                    var self = {},name = "foo";
                    function _wroking() {
                        console.log("working");
                    }
                    self.hello = function () {
                        console.log("hello " + name);
                    }
                    self.doWork = function () {
                        _wroking();
                    }
                    return self;
                })();
    复制代码

    这样内部方法和外部方法就有明确的区分,可以看下Zepto的大体结构:

    复制代码
    var Zepto = (function() {
            var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter,
                    zepto = {}, camelize, uniq;
                    //...........
                    function isObject(obj) { return type(obj) == "object" }
                    //..........
                    zepto.matches = function (element, selector) {
                    //...
                    }
                    zepto.init = function(selector, context) {
                    //......
                    }
                    $ = function (selector, context) {
                        return zepto.init(selector, context)
                    }
                    function extend(target, source, deep) {
                     //...
                    }
                    $.extend = function (target) {
                    }
                })()
    
                window.Zepto = Zepto
                window.$ === undefined && (window.$ = Zepto)
    复制代码

    内部定义了zepot对象,并注意到有一个extend方法,便于后面扩展zepot的这个模块,当然也可以扩展其他的模块。target是指定的。

    复制代码
      function extend(target, source, deep) {
        for (key in source)
          if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
            if (isPlainObject(source[key]) && !isPlainObject(target[key]))
              target[key] = {}
            if (isArray(source[key]) && !isArray(target[key]))
              target[key] = []
            extend(target[key], source[key], deep)
          }
          else if (source[key] !== undefined) target[key] = source[key]
      }
    
      // Copy all but undefined properties from one or more
      // objects to the `target` object.
      $.extend = function(target){
        var deep, args = slice.call(arguments, 1)
        if (typeof target == 'boolean') {
          deep = target
          target = args.shift()
        }
        args.forEach(function(arg){ extend(target, arg, deep) })
        return target
      }
    复制代码

    再扩展子模块的时候就方便了:

    复制代码
                ;(function ($) {
                    //...
                    $.event = { add: add, remove: remove }
                    $.proxy = function(fn, context) {
                      //...
                    }
                })(Zepto);
    复制代码

    现代的模块机制

    AMDCMD都是将模块的定义封装进了一个友好的API。就是require.js和sea.js中的define方法。先看一个简单的现代模块实现:

    复制代码
    var MyModules = (function () {
                    var modules = {};
    
                    function define(name, deps, impl) {
                        for (var i = 0; i < deps.length; i++) {
                            deps[i] = modules[deps[i]];
                        }
                        modules[name] = impl.apply(impl, deps);
                    }
    
                    function get(name) {
                        return modules[name];
                    }
    
                    return {
                        define: define,
                        get: get
                    }
                })();
                MyModules.define("bar", [], function () {
                    function hello(who) {
                        return "Let me introduce: " + who;
                    }
                    return {
                        hello: hello
                    };
                });
                MyModules.define("foo", ["bar"], function (bar) {
                    var hungry = "jazz";
    
                    function awsome() {
                        console.log(bar.hello(hungry));
                    }
    
                    return {
                        awsome: awsome
                    }
                });
                var bar = MyModules.get("bar");
                var foo = MyModules.get("foo");
    
                console.log(bar.hello("hippo"));//Let me introduce: hippo
                foo.awsome();//Let me introduce: jazz
    复制代码

    MyModules的define方法包含name,deps,impl三个参数,name表示是模块名称,deps表示是依赖项,impl表示实现。关键是modules[name] = impl.apply( impl, deps );这一句,上面的for循环将一个模块名称数组先转成一个包含具体模块的数组,然后apply给具体的实现方法。相当于是注入了依赖项。注意到定义foo模块的时候,依赖了bar模块,只需要在deps这个参数加入[“bar”]即可。

    在sea.js中,模块的状态做了区分:

    复制代码
    var STATUS = Module.STATUS = {
      // 1 - The `module.uri` is being fetched 相当于初始化
      FETCHING: 1,
      // 2 - The meta data has been saved to cachedMods 缓存在cacheMods
      SAVED: 2,
      // 3 - The `module.dependencies` are being loaded
      LOADING: 3,
      // 4 - The module are ready to execute
      LOADED: 4,
      // 5 - The module is being executed
      EXECUTING: 5,
      // 6 - The `module.exports` is available
      EXECUTED: 6
    }
    复制代码

    看下define方法:

     View Code

    save的内部是通过Module.get 缓存起uri和deps(依赖项)构建的Module对象。

    Module.get = function(uri, deps) {
      return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))
    }

    require也是通Module.get来获取模块:

    seajs.require = function(id) {
      return (cachedMods[resolve(id)] || {}).exports
    }

    不像例子中是一次性加载模块,sea.js可以在需要的地方再加载对应的模块。

    ES6中的模块

    以上的模块都是基于函数的,API在运行时都可以被修改。ES6中为模块增加了一级语法支持,通过模块系统进行加载时,ES6会将文件独立的模块来处理。使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

    // ES6模块
    import { stat, exists, readFile } from 'fs';

    上面代码的实质是从fs模块加载3个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。

    下面是一个circle.js文件,它输出两个方法areacircumference

    复制代码
    // circle.js
    
    export function area(radius) {
      return Math.PI * radius * radius;
    }
    
    export function circumference(radius) {
      return 2 * Math.PI * radius;
    }
    复制代码

    现在,加载这个模块。

    复制代码
    // main.js
    
    import { area, circumference } from './circle';
    
    console.log('圆面积:' + area(4));
    console.log('圆周长:' + circumference(14));
    复制代码

    上面写法是逐一指定要加载的方法,整体加载的写法如下。

    import * as circle from './circle';

    这语法让人有点激动。而且是不允许修改的,这样就很大的确保了接口的稳定性。

    import * as circle from './circle';
    
    // 下面两行都是不允许的 
    circle.foo = 'hello';
    circle.area = function () {};

    小结:模块在JavaScript运用很广泛,好的模块构建能确定一个清晰的结构,有助于分工和维护。

    参考文档:

    ES6 Module: http://es6.ruanyifeng.com/#docs/module

    CMD规范:https://github.com/seajs/seajs/issues/242

    阅读书籍:

    资源在读书群中。

  • 相关阅读:
    DNNClassifier 深度神经网络 分类器
    浏览器对MP4视频 帧宽度 高度的兼容性
    UnicodeEncodeError:'latin-1' codec can't encode character
    文件夹下 文件计数
    the largest value you actually can transmit between the client and server is determined by the amount of available memory and the size of the communications buffers.
    the “identity” of an object
    广告特征 用户特征
    如果一个维度全覆盖,则有效维度应该对该维度全覆盖
    a high-level neural networks AP
    使用 LDA 挖掘的用户喜好主题
  • 原文地址:https://www.cnblogs.com/libin-1/p/6401021.html
Copyright © 2020-2023  润新知