本人没有学过面向对象的语言,对于模块化开发,也是基于对前端项目制作时的经验、阅读其他人的文章以及对javascript学习的,因此本文仅为记录本人目前所感受到的问题,如有错误,恳请指正。
什么是模块化
模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干模块的过程,有多种属性,分别反映其内部特性。模块化用来分割,组织和打包软件。每个模块完成一个特定的子功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求的功能。
也就是说,分析一个复杂的问题,将其拆分成一系列子问题,然后再组合再一起。引用叶小钗老师的例子理解requireJS-实现一个简单的模块加载器
模块化的简单实例
到手的薪水与绩效、公积金、个人所得税、基本工资有关。最开始,我可能会这么写:
1 2 3 4 5 6 7 8 9
| var mySalary, salary = 1000, performanceCoefficient = 0.2, companyReserve = salary * 0.2, incomeTax = salary * 0.2; mySalary = salary + salary * performanceCoefficient; mySalary = mySalary - companyReserve - incomeTax;
|
看起来很不错,是么?但是,绩效、公积金、税的计算不会那么简单,于是乎,以个人所得税为例我做个修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var mySalary, salary = 1000, performanceCoefficient = 0.2, companyReserve = salary * 0.2, incomeTax; var incomeTaxBase = salary - 3500; if(incomeTaxBase <= 0){ incomeTax =0; }else if(incomeTaxBase > 0 && incomeTaxBase <= 1500){ incomeTax = 0.03 * incomeTaxBase; }else if(incomeTaxBase >1500 && incomeTaxBase <= 4500){ incomeTax = 0.03 * 1500 + 0.1 * (incomeTaxBase - 1500); }else if{} mySalary = salary + salary * performanceCoefficient; mySalary = mySalary - companyReserve - incomeTax;
|
可以发现,整个程序变得十分复杂,若哪里计算有问题,都不方便检查。而这仅仅是改变了个人所得税收入的计算。(当然这个计算公式也有问题)
因此,我们会将不同的计算封装到函数中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| var performanceCoefficient = function () { return 0.2; }; var companyReserve = function (salary) { return salary * 0.2; }; var incomeTax = function (salary) { return salary * 0.2; }; var salary = 1000; var mySalary = salary + salary * performanceCoefficient(); mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary));
|
这样,当我们需要计算工资的时候,只需要调用不同的函数,就能得到所需要的值,而不用考虑其中的细节。如若某种参数的计算过程发生改变,也只需要更改函数中的内容,而函数的调用方法不会改变。
以上的方法,有个很明显的不足,就是对全局的污染。考虑到javascript中,函数拥有自己的作用域,我们可以通过以下方式进行封装:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| var module1 = (function(){ var performanceCoefficient = function () { return 0.2; }; var companyReserve = function (salary) { return salary * 0.2; }; var incomeTax = function (salary) { return salary * 0.2; }; var salary = 1000; var mySalary = salary + salary * performanceCoefficient(); mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary)); return { getSalary:function(salary){ var mySalary = salary + salary * performanceCoefficient(); mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary)); return mySalary; } }; })(); module1.getSalary(1000);
|
我这样的写法,忽略了可重用性(工资各参数的计算仅能在此模块中使用,无法在其他模块中使用),且当参数计算过于复杂,整个模块会特别大。因此,比较好的方式是将各参数的计算分离到不同的javascript文件中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script src="companyReserve.js" type="text/javascript"></script> <script src="incomeTax.js" type="text/javascript"></script> <script src="performanceCoefficient.js" type="text/javascript"></script> <script type="text/javascript"> var salary = 1000; var mySalary = salary + salary * performanceCoefficient(); mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary)); console.log(mySalary); </script>
|
这种方式,要求我们按照正确顺序引入各文件。正如叶小钗老师所说,这种方式并不是按照模块划分,而是按照文件划分。我们需要一种方式,来处理各模块的引入,依赖问题。
RequireJS
RequireJS 是一个JavaScript模块加载器。它非常适合在浏览器中使用, 它非常适合在浏览器中使用,但它也可以用在其他脚本环境, 就像 Rhino and Node. 使用RequireJS加载模块化脚本将提高代码的加载速度和质量。
RequireJS遵循AMD规范。
引入模块
首先,在html文件中引入requireJS,并指定入口函数。
1
| <script src="scripts/require.js" data-main="scripts/main.js"></script>
|
其中main.js
的路径相对于html的路径。
配置requireJS
在main.js
中,需要对requireJS进行配置,如基本路径、模块路径映射等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| requirejs.config({ baseUrl:'./', paths: { jquery: 'lib/jquery', validate: 'app/validate' }, shim: { 'backbone': { deps: ['underscore', 'jquery'], exports: 'Backbone' }, 'underscore': { exports: '_' }, 'foo': { deps: ['bar'], exports: 'Foo', init: function (bar) { return this.Foo.noConflict(); } } }); requirejs(['jquery', 'canvas', 'app/sub'],function($,canvas, sub) { });
|
定义模块
模块定义的格式为:define(模块名,依赖,回调函数);
1 2 3
| define("foo/title",["my/cart", "my/inventory"],function(cart, inventory) { });
|
CommonJS
服务器端的模块化编程,遵循commonJS规范。它不适合浏览器端,因为它加载模块是同步的,会阻塞页面。
使用
1 2 3
| var math = require('math'); math.add(2,3);
|
定义
1 2 3
| var moduleName = {}; module.exports = moduleName;
|
编写兼容各标准的插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| (function (root) { var moduleName = {}; if (typeof define === 'function' && define.amd) { define([], function () { return moduleName; }); } else if (typeof exports === 'object') { module.exports = moduleName; } else { root.moduleName = moduleName; } })(this);
|
参考
理解requireJS-实现一个简单的模块加载器
Javascript模块化编程(一):模块的写法
RequireJS