• 好文推荐系列-------(5)js模块化编程


    本文主要来源于阮一峰的<Javascript模块化编程>系列文章整合,原文地址:http://www.ruanyifeng.com/blog/2012/10/javascript_module.html

    个人认为写的非常清楚,故收藏于此。

    随着网站逐渐变成"互联网应用程序",嵌入网页的Javascript代码越来越庞大,越来越复杂。

    网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等......开发者不得不使用软件工程的方法,管理网页的业务逻辑。

    Javascript模块化编程,已经成为一个迫切的需求。理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。

    但是,Javascript不是一种模块化编程语言,它不支持""(class),更遑论"模块"(module)了。(正在制定中的ECMAScript标准第六版,将正式支持"类"和"模块",但还需要很长时间才能投入实用。)

    Javascript社区做了很多努力,在现有的运行环境中,实现"模块"的效果。本文总结了当前"Javascript模块化编程"的最佳实践,说明如何投入实用。虽然这不是初级教程,但是只要稍稍了解Javascript的基本语法,就能看懂。

    一、原始写法

    模块就是实现特定功能的一组方法。

    只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。

    function getName(){
        console.log("hello world");
    }
    function setName(name){
        console.log(name);
    }

    上面的函数getName()和setName(),组成一个模块。使用的时候,直接调用就行了。

    这种做法的缺点很明显:"污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。

    二、对象写法

    为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。

    var person=new Object({
        name : "hello word",
        getName: function(){
            console.log(this.name);   //hello word
        },
        setName:function(pname){
            console.log(pname)
        }
    });
    person.getName();

    上面的函数getName()和setName(),都封装在person对象里,使用的时候就调用这个对象的方法,但是这样做有个最大的缺点就是暴露了所有的模块成员,内部的状态可以被外部改写,外部代码可以直接改写对象的属性值,这是很危险的操作。

    var person=new Object({
        name : "hello word",
        getName: function(){
            console.log(this.name);   
        },
        setName:function(pname){
            console.log(pname)
        }
    });
    person.name="alisa";
    person.getName();   //alisa

    三、立即执行函数写法

    使用"立即执行函数"(Immediately-Invoked Function Expression,IIFE),可以达到不暴露私有成员的目的。

    var person = (function(){
        var name="hello word";
        var getName=function(){
            console.log(this.name);
        };
        var setName=function(pname){
            console.log(pname);
        };
        return {
            getName:getName,
            setName:setName
        };
    })();
    person.getName();  //undefined

    使用上面的方法就无法读取函数内部的变量,其实这种写法已经很接近模块化了,后续将对这种写法进行再加工.

    四、模块的规范

    为什么要使用模块?

    因为有了模块,我们可以很方便的使用别人的代码,想用什么功能就使用什么模块。

    但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!考虑到Javascript模块现在还没有官方规范,这一点就更重要了。

    目前,通行的Javascript模块规范共有两种:CommonJSAMD。我主要介绍AMD,但是要先从CommonJS讲起。

    五、CommonJS

    2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。

    这标志"Javascript模块化编程"正式诞生。因为老实说,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。

    node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。

      var math = require('math');

    然后,就可以调用模块提供的方法:

      var math = require('math');

      math.add(2,3); // 5

    因为这个系列主要针对浏览器编程,不涉及node.js,所以对CommonJS就不多做介绍了。我们在这里只要知道,require()用于加载模块就行了。

    六、浏览器环境

    有了服务器端模块以后,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。

    但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。还是上一节的代码,如果在浏览器中运行,会有一个很大的问题,你能看出来吗?

      var math = require('math');

      math.add(2, 3);

    第二行math.add(2, 3),在第一行require('math')之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。

    这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。

    因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。

    七、AMD

    AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

    AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:

      require([module], callback);

    第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:

      require(['math'], function (math) {

        math.add(2, 3);

      });

    math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。

    目前,主要有两个Javascript库实现了AMD规范:require.jscurl.js

    八、为什么要用require.js?

    最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。

      <script src="1.js"></script>
      <script src="2.js"></script>
      <script src="3.js"></script>
      <script src="4.js"></script>
      <script src="5.js"></script>
      <script src="6.js"></script>

    这段代码依次加载多个js文件。

    这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

    由于不是JavaScript原生支持,使用AMD规范进行页面开发需要用到对应的库函数,也就是大名鼎鼎RequireJS,实际上AMD 是 RequireJS 在推广过程中对模块定义的规范化的产出

    require.js的诞生,就是为了解决这两个问题:

    (1)实现js文件的异步加载,避免网页失去响应;

    (2)管理模块之间的依赖性,便于代码的编写和维护。

    requireJS定义了一个函数 define,它是全局变量,用来定义模块

    define(id?, dependencies?, factory);
    define(["dependency"],function(){
        var name="hello world";
        function getName(){
            console.log(name);
        };
        function setName(pname){
            console.log(pname);
        };
        return {
            getName:getName,
            setName:setName
        
        }
    });
    1. id:可选参数,用来定义模块的标识,如果没有提供该参数,脚本文件名(去掉拓展名)
    2. dependencies:是一个当前模块依赖的模块名称数组
    3. factory:工厂方法,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值
  • 相关阅读:
    javaweb请求编码 url编码 响应编码 乱码问题 post编码 get请求编码 中文乱码问题 GET POST参数乱码问题 url乱码问题 get post请求乱码 字符编码
    windows查看端口占用 windows端口占用 查找端口占用程序 强制结束端口占用 查看某个端口被占用的解决方法 如何查看Windows下端口占用情况
    javaWeb项目中的路径格式 请求url地址 客户端路径 服务端路径 url-pattern 路径 获取资源路径 地址 url
    ServletRequest HttpServletRequest 请求方法 获取请求参数 请求转发 请求包含 请求转发与重定向区别 获取请求头字段
    HttpServletResponse ServletResponse 返回响应 设置响应头设置响应正文体 重定向 常用方法 如何重定向 响应编码 响应乱码
    Servlet主要相关类核心类 容器调用的过程浅析 servlet解读 怎么调用 Servlet是什么 工作机制
    linq查询语句转mongodb
    winddows rabbitmq安装与配置
    Redis For Windows安装及密码
    出现,视图必须派生自 WebViewPage 或 WebViewPage错误解决方法
  • 原文地址:https://www.cnblogs.com/alisayuan/p/5169259.html
Copyright © 2020-2023  润新知