zoukankan      html  css  js  c++  java
  • JavaScript模块化的演变

    前情回顾:

    自执行函数(IIFE):

    作用:马上执行这个函数,自执行函数(IIFE),不易读

    (function(x){console.log(x);})(3);
    

    易读版本:

    (function(x){
        return x *x;
    })(3);
    

    闭包引申:

    回顾:

    function create_counter(initial){
        var x = initial || 0;  //如果initial没有值,那么采用后面的0
        return {
            //对象
            inc:function(){
                x+=1;
                return x;
            }
        }
    }
    var c1 = create_counter();
    console.log(c1.inc());
    console.log(c1.inc());
    console.log(c1.inc());
    
    
    var c2 = create_counter(10);
    console.log(c2.inc());
    console.log(c2.inc());
    console.log(c2.inc()); */
    

    箭头函数:

    function (x){
        return x * x;
    }
    

    上述代码等价于下面:

    x => x*x;
    

    箭头函数的无参、单参、双参、多参的格式:

    //无参
    ()=>3;
    //单参
    x =>{
        if(x > 0){
            return x*x;
        }else{
            return -x*x;
        }
    }
    //双参
    (x,y) => x*x+y*y;
    //多参
    (x,y,...rest)=>{
    
    }
    

    this指向的引入以及发展:

    this的指向在有无use strict会不同的,我们通过几段不同的代码段引入this以及this指向的发展。

    'use strict';
    //1版-->正常使用
    var xiaoming = {
        name:'小明',
        birth:2000,
        age:function(){
            var y = new Date().getFullYear();
            return y - this.birth;
        }
    };
    console.log(xiaoming.age()); 
    
    //2版:
    'use strict';
    /* 
      严格模式:
      xiaoming.age() 可以得到结果,
      getAge()显示报错--> Cannot read property 'birth' of undefined
    */
    function getAge(){
        var y = new Date().getFullYear();
        return y - this.birth;
    }
    var xiaoming = {
        name:'小明',
        birth:2000,
        age:getAge
    };
    console.log(xiaoming.age());
    console.log(getAge()); 
    
    

    前两个版本暂时还是没有引出this以及指向,下面看第三个版本,这个版本会有较大的改变,在不在strict中的结果也会不一样。

    'use strict';
    /* 
      严格模式下:会报错,Cannot read property 'birth' of undefined
      非严格模式下:NaN  not a number,浏览器不会爆红,可能指向window
     */
    var obj = {
        birth:2000,
        getAge:function(){
            function GetAgeFormBirth(){
                var y = new Date().getFullYear();
                return y - this.birth;
            }
            return GetAgeFormBirth();
        }
    };
    console.log(obj.getAge()); 
    

    这是js的遗留问题,为了解决这个问题,我们可以使用变量保存this的指向,如版本四:

    'use strict';
    var obj = {
        birth:2000,
        getAge:function(){
            //保存thisz指向
            var that = this;
            function GetAgeFormBirth(){
                var y = new Date().getFullYear();
                return y - that.birth;
            }
            return GetAgeFormBirth();
        }
    };
    console.log(obj.getAge());
    

    但是这种表述方式比较麻烦,代码量也有些多,我们可以采用箭头函数来实现:

    //这是个对象
    'use strict';
    var obj = {
        birth:2000,
        getAge:function(){
            var b = this.birth;
            var fn = ()=> new Date().getFullYear()-this.birth;
            return fn();
        }
    };
    console.log(obj.getAge()); 
    

    模块化演变:

    模块化演变是为了初学者一步一步实现和解决代码的冗余、复用性、命名污染问题。

    模块演变1:

    缺点已在代码块中标明

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <!-- html --》"" -->
        <!-- js --》'' -->
        <!-- 使用内嵌脚本 -->
        <!-- 
            缺点:
            复用性 很低 
            1.缺乏依赖解析
            2.全局命名空间污染的问题
        -->
        <h1>
            the answer is <span id="answer"></span>
        </h1>
        <script>
            function add(a,b){
                return a+b;
            }
            function reduce(arr,iteratee){
                var index=0,length = arr.length,memo = arr[index];
    	index+=1;
                for(index =0;index < length;index++){
                    memo = iteratee(memo,arr[index]);
                }
                return memo;
            }
            function sum(arr){
                return reduce(arr,add);
            }
            var values = [1,2,3,4,5,6,7,8,9];
            var answer = sum(values);
            document.getElementById("answer").innerHTML = answer;
        </script>
    </body>
    </html>
    

    模块演变2:

    add.js

    function add(a,b){
        return a+b;
    }
    

    reduce.js

    function reduce(arr,iteratee){
        var index=0,length = arr.length,memo = arr[index];
        index+=1;
        for(index =0;index < length;index++){
            memo = iteratee(memo,arr[index]);
        }
        return memo;
    }
    

    sum.js

    function sum(arr){
        return reduce(arr,add);
    }
    

    main.js

    var values = [1,2,3,4,5,6,7,8,9];
    var answer = sum(values);
    document.getElementById("answer").innerHTML = answer;
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <h1>
            the answer is <span id="answer"></span>
        </h1>
        <!-- script标签引入JavaScript -->
        <!-- 缺点:
            必须保证js的引用顺序正确
            同样缺乏依赖解析
            同样有命名冲突的问题
        -->
        <script src = "./add.js"></script>
        <script src = "./reduce.js"></script>
        <script src="./sum.js"></script>
        <script src = "./main.js"></script>
    </body>
    </html>
    

    模块演变3:

    myApp.js

    // 空对象
    var myApp = {
    };
    

    add.js

    // 立即执行函数
    (function (){
        // 将add放入myApp中
        myApp.add = function(a,b){
            return  a+b;
        }
    })();
    

    reduce.js

    // 立即执行函数IIFE
    (function (){
        // 将reduce放入myApp中
        myApp.reduce = function(arr,iteratee){
            var index=0,length = arr.length,memo = arr[index];
    	index+=1;
            for(;index < length;index++){
                memo = iteratee(memo,arr[index]);
            }
        return memo;
        }
    })();
    

    sum.js

    // 立即执行函数
    (function (){
        // 将sum放入myApp中
        myApp.sum = function(arr){
            return  myApp.reduce(arr,myApp.add);
        }
    })();
    

    main.js

    /**
     * @description: 
     * @param {*}
     * @return {*}
     */
    /* (function(){
        var values = [1,2,3,4,5,6,7,8,9];
        var answer = myApp.sum(values);
        document.getElementById("answer").innerHTML = answer;
    }); */
    
    (function(app){
        var values = [1,2,3,4,5,6,7,8,9];
        var answer = myApp.sum(values);
        document.getElementById("answer").innerHTML = answer;
    })(myApp);
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <h1>
            the answer is <span id="answer"></span>
        </h1>
    
        <script src="./myapp.js"></script>
        <script src="./reducec.js"></script>
        <script src="./sum.js"></script>
        <script src="./add.js"></script>
        <script src="./main.js"></script>
        
    </body>
    </html>
    

    模块演变4:

    采用require.js来实现。

    为什么要使用require.js来实现:

    在正式开发的项目中,随着js的外部引用文件越来越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序,依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

    在开发中,有一个网页三秒原则

    网页三秒原则:

    当用户的在请求一个网站的时候,响应时间超过三秒钟,大部分用户将关闭或者重新刷新网页,用户体验很不爽。

    所以require.js就解决上述问题:

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

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

    require.js的AMD格式规范:

    // main.js
    
    define(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
    
        // some code here
    
    });
    

    define()函数接受的参数。

    1. 第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;

    2. 第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。

    3. 模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中

    4. 如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,且指明该模块的依赖性。

    define()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

    以下是模块演变4的实现代码:

    index.js

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <h1>
            the answer is <span id="answer"></span>
        </h1>
    	<!-- 入口点是main -->
    	<!-- require加载了main.js的依赖,也会加载它依赖的依赖 -->
    	<!-- 带来的问题:
    		1. 降低一些性能
    	 -->
        <script data-main="main" src = "require.js"></script>
    </body>
    </html>
    

    data-main属性的作用是,指定网页程序的主模块

    main.js

    define(['sum'],function(sum){
    		var value = [1,2,3,4,5,6,7,8,9];
    		var answer = sum(value);
    		document.getElementById('answer').innerHTML = answer;
    })
    

    sum.js

    define([
        'add',
        'reduce'
    ], function(add, reduce) {
        var sum = function(arr){
            return reduce(arr,add);
        }
        return sum;
        
    });
    

    reduce.js

    define([], function(arr,iteratee){
        var reduce = function(arr,iteratee){
            var index=0,length = arr.length,memo = arr[index];
    	index+=1;
            for(;index < length;index++){
                memo = iteratee(memo,arr[index]);
            }
            return memo;
        }
        return reduce;
    });
    

    add.js

    define([],function(){
        var add = function(a,b){
            return a+b;
        }
    	return add;
    })
    

    分析上述代码的执行流程:

    1. 先加载index.html以及require.js文件,找到模块的主入口(main.js)
    2. 加载main.js,由于main中依赖sum.js
    3. 再加载sum.js,sum中依赖add.js以及reduce.js
    4. 再加载add.js以及reduce.js
    5. 最后全部依赖执行完成后,回调得到的结果

    执行效果以及文件加载顺序的观察:

    image-20210712142033851

    模块演变5:

    基于commonJS规范以及browserify浏览器端的模块演变

    什么是browserify:

    browserify是专注于解决按照CommonJS规范书写的模块能够在浏览器中使用问题的构建工具。

    CommonJS规范:

    1. 每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见
    2. 每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

    下载方式:

    npm install -g browserify
    

    代码如下:

    index.js

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="utf-8">
    		<title></title>
    	</head>
    	<body>
    		<h1>
    			The Answer is <span id="answer"></span>
    		</h1>
    		<!--  browserify .main.js -o bundle.js   打包-->
    		<script src="bundle.js"></script>
    	</body>
    </html>
    
    

    注意点:引用的js的文件是与我们打包命令时 -o后面名字对应的。

    main.js

    var sum = require('./sum');
    var values = [1,2,3,4,5,6,7,8,9];
    var answer = sum(values);
    document.getElementById("answer").innerHTML = answer;
    

    sum.js

    var reduce = require('./reduce');
    var add = require('./add');
    
    module.exports = function(arr){
    	return reduce(arr,add);
    };
    

    reduce.js

    module.exports = function reduce(arr,iteratee){
            var index=0,length = arr.length,memo = arr[index];
    	index+=1;
            for(;index < length;index++){
                memo = iteratee(memo,arr[index]);
            }
            return memo;
        };
    

    add.js

    module.exports = function add(a,b){
    	return a+b;
    };
    

    打包方式:

    browserify .main.js -o bundle.js   //window中
    

    效果图:

    image-20210712125510595

    运行查看加载信息:

    image-20210712144048436

    可以与require.js的加载方式做对比,理解两者的不同以及相同的地方。

    扩展:

    require.js VS browserify

    • require.js是模块加载器;browserify是预编译工具
    • require.js遵循的是AMD规范;browserify遵循的是CommonJS规范
    • require.js是在浏览器端运行期分析依赖;browserify在服务器端编译期就进行了依赖分析,通过每个模块的外部接口获取信息

    结束:

    如果你看到这里或者正好对你有所帮助,希望能点个关注或者推荐,感谢;

    有错误的地方,欢迎在评论指出,作者看到会进行修改。

  • 相关阅读:
    菜鸟攻城狮4(基本语法)
    Spring 3.x 企业引用开发实战(陈雄华/林开雄)
    进击的菜鸟问题1(设置checkbox全选)
    Maven实战
    菜鸟攻城狮3(Holle World)
    菜鸟攻城狮2(JAVA开发环境)
    git基本使用
    跨域问题及解决跨域的方案
    原生ajax相关概念
    php服务器语言,MySQL数据库以及php怎么操作数据库
  • 原文地址:https://www.cnblogs.com/xbhog/p/15001650.html
Copyright © 2011-2022 走看看