概念:利用闭包的保护机制,将内部的私有变量通过对象的方式暴露出去。对象都是单独的实例
    基于闭包的方式解决全局污染问题「无法直接调用其它模块下的方法」
    window.xxx=xxx 暴露的API不易过多
    基于对象分组的特点:把当前需要供别人调用的API/信息放置在一个堆中,让utils指向这个堆,后期基于utils就可以访问到堆中的API
    高级单例模式[基于闭包来管理的单例模式]:
    单独的实例:基于单独的实例,来管理自己模块下的内容,保证不冲突。
    namespace: 命名空间
    最早期的模块化:基于闭包的方式实现模块下的方法私有化,也可以基于单例实现API之间的共用。

    1. // 公共方法
    2. let utils = (function () {
    3. const debounce = function debounce(){}
    4. const toType = function toType(){}
    5. return {
    6. debounce,
    7. toType
    8. }
    9. })()
    10. // searchModule命名空间,可以通过直接调用utils中的方法
    11. let searchModule = (function (){
    12. const submit = function submit(){}
    13. utils.debounce(submit)
    14. return {
    15. submit
    16. }
    17. })()

    基于单例模式的命令模式

    1. let weatherModule = (function () {
    2. let city = '北京';
    3. const queryData = function queryData(callback) {
    4. let data = null;
    5. // ...
    6. callback && callback(data);
    7. };
    8. const bindHTML = function bindHTML() {};
    9. const changeCity = function changeCity() {};
    10. return {
    11. init() {
    12. // init中管控当前模块下各个业务功能的执行顺序「大脑」 -> 命令设计模式
    13. queryData(function () {
    14. bindHTML();
    15. changeCity();
    16. });
    17. }
    18. };
    19. })();
    20. weatherModule.init();

    基于单例模式完成的一个倒计时抢购示例

    1. /**
    2. * 两个时间
    3. * +目标时间:18:00:00
    4. * +当前时间
    5. * 目标时间-当前时间=时间差
    6. * 问题:需要通过请求获取服务器时间[统一获取响应头中的Date],客户端时间可以修改
    7. * 获取服务器时间会存在偏差问题 -> HEAD请求 ajax状态码为2
    8. */
    9. let countDownModule = (function () {
    10. let timeBox = document.querySelector('#box .time')
    11. let serviceTime = 0, targetTime = +new Date('2021/01/13 10:48:00'), timer = null
    12. // 获取服务器时间
    13. const queryServiceTime = function queryServiceTime() {
    14. return new Promise(resolve => {
    15. let xhr = new XMLHttpRequest
    16. xhr.open('HEAD', '/')
    17. xhr.onreadystatechange = () => {
    18. if (xhr.readyState === 2 && (xhr.status >= 200 && xhr.status < 300)) {
    19. let time = xhr.getResponseHeader('Date')
    20. resolve(+new Date(time))
    21. }
    22. }
    23. xhr.send(null)
    24. })
    25. }
    26. // 补0
    27. const supplyZone = function supplyZone(val) {
    28. val = +val
    29. return val < 10 ? `0${val}` : val
    30. }
    31. // 倒计时计算
    32. const computed = function computed() {
    33. let diff = targetTime - serviceTime,
    34. hours,
    35. min,
    36. sec
    37. if (diff <= 0) {
    38. timeBox.innerHTML = '开始抢购'
    39. clearInterval(timer)
    40. return
    41. }
    42. hours = Math.floor(diff/(1000*60*60))
    43. diff = diff - hours * 60 *60*1000
    44. min = Math.floor(diff / (60*1000))
    45. diff = diff - min * 60 *1000
    46. sec = Math.floor(diff / 1000)
    47. timeBox.innerHTML = `${supplyZone(hours)}:${supplyZone(min)}:${supplyZone(sec)}`
    48. }
    49. return {
    50. async init() {
    51. serviceTime = await queryServiceTime()
    52. computed()
    53. // 设置定时器
    54. timer = setInterval(() => {
    55. computed()
    56. serviceTime += 1000
    57. }, 1000)
    58. }
    59. }
    60. })()
    61. countDownModule.init()