模块化/单例设计模式

  1. let name = 'Erina';
  2. let age = 21;
  3. let sex = '女‘;
  4. let friends = ["秋含","白静","汪林"];
  5. let name = 'qiuhan';
  6. let age = 20;
  7. let sex = '男';
  8. let friends = ['erina',"白静","汪林"];

image.png
这样会重复定义,会报错,在没有对像和函数的情况下,经常会出现“全局变量污染”的现象。

方案一:对象

分组的作用,把描述同一个事物的属性和方法都放在相同的空间(堆内存)中

  1. let person1 = {
  2. name: 'Erina',
  3. age: '21'
  4. }
  5. let person2 = {
  6. name: 'Qiuhan',
  7. age: '20'
  8. }

方案二:闭包/私有上下文

保护作用,保护私有变量和全局变量不产生冲突,方式全局变量污染。

  1. (function(){
  2. let name = 'Erina';
  3. let age = 21;
  4. let sex = '女‘;
  5. let friends = ["秋含","白静","汪林"];
  6. (function(){
  7. let name = 'qiuhan';
  8. let age = 20;
  9. let sex = '男';
  10. let friends = ['erina',"白静","汪林"];
  11. })()
  1. // 真实业务开发
  2. // A
  3. let AModule = (function () {
  4. let step = 0;
  5. function fn() {}
  6. function query() {}
  7. // 我们想把私有的东西暴露出去共外面调用(API),供B调用
  8. // 1. window.xxx=xxx 瑕疵:不能给全局下暴露太多的方法,否则又出现“全局变量污染”
  9. // window.query = query; 在B那边直接调用query()
  10. // 2. 基于返回对象(分组)的方法,把需要暴露的API,都放置到同一个空间下
  11. // AModule等于自执行函数执行的返回的结果->对象
  12. // 如果需要把一些私有的属性方法和私有变量暴露到外面使用,通过return一个对象的方式,把对象赋值给AModule
  13. // 一个私有上下文下,它里面的某一个堆,这个堆地址被当前上下文以外的全局的AModule占用,这个私有上下文就不会
  14. // 被释放,形成了一个闭包。
  15. // AModule的方式就是单例设计模式
  16. return {
  17. // query:query
  18. query,
  19. step
  20. };
  21. })();
  22. // B
  23. (function () {
  24. let step = 0;
  25. function fn() {}
  26. // query(); // window.query();
  27. AModule.query();
  28. })();

终极处理方案:单例设计模式(最早的模块化编程方式)

  • 原理:利用闭包的保护及对象的分组特征,结合在一起实现的
  • 工具库:大量公共的方法(有些方法是内部用的,有些是可以供外面调用的)
  • 单例:单独的实例,每一个实例都是独立开来的,里面有很多自己的私有属性和方法

utils和SearchModule返回的都是对象(单独的空间,单独的实例),每一个对象都是Object的单独实例。
let utils = {};
let SearchModule = {};

  • 每一个对象都是Object的单独实例
    • let obj={}; 字面量方式
    • let obj=new Object(); 构造函数方式
  • 命名空间:俗称“对象名”(utils也叫命名空间)
    • 分组特征 (想用明命空间就是想用分组特征)

utilsSearchModule 返回的都是对象,只不过还利用了闭包形成了私有的闭包,让部分属性方法私有,让部分属性方法公有。

  1. let utils = (function () {
  2. function getCss() {}
  3. function setCss() {}
  4. function css() {
  5. getCss();
  6. setCss();
  7. }
  8. return {
  9. css
  10. };
  11. })();
  12. // 搜索模块
  13. let SearchModule = (function () {
  14. let keyword = "";
  15. function handle() {}
  16. function query() {}
  17. utils.css();
  18. return {
  19. handle,
  20. // init:function(){}
  21. init() {
  22. // 控制业务板块中我们先执行谁,再执行谁:控制业务逻辑处理顺序的 “命令模式”
  23. query();
  24. handle();
  25. }
  26. };
  27. })();
  28. SearchModule.init();
  29. // 资讯模块
  30. let NewsModule = (function () {
  31. let data = [];
  32. function query() {}
  33. utils.css();
  34. SearchModule.handle();
  35. return {
  36. init() {
  37. }
  38. };
  39. })();
  40. NewsModule.init();

PS:构造函数模式 let p = new Person() ,不是单例模式。

JQuery源码分析

  1. (function (global, factory) {
  2. // ...
  3. })(typeof window !== "undefined" ? window : this, function (window, noGlobal){});
  1. var A = typeof window !== "undefined" ? window : this;
  2. // 利用暂时性死区:一个未被声明的变量在typeof检测的时候不会报错,只是返回"undefined"
  3. // ->检测window是否存在
  4. // JS在浏览器中执行:是存在window的(window === GO)
  5. // JS在NODE中执行:不存在window,全局对象是global
  6. // ->A最后的结果根据执行所在的环境不一样,结果也不一样:浏览器下是window,node下是global
  7. var B = function (window, noGlobal){
  8. // 如果是在浏览器环境中执行的JS代码:
  9. // window: window
  10. // noGlobal: undefined(没有传值)
  11. // 如果是在NODE环境下执行
  12. // window: global
  13. // noGlobal: true
  14. "use strict";
  15. var jQuery = function (selector, context) {};
  16. if(typeof noGlobal === "undefined") {
  17. // 浏览器环境下:暴露给全局的俩个变量(值都是私有的jQuery)
  18. // jQuery
  19. // $
  20. window.jQuery = window.$ = jQuery;// $() = jQuery()
  21. }
  22. return jQuery;
  23. };
  24. "use strict";
  25. // global: window / global
  26. // factory: B
  27. (function (global, factory) {
  28. // 验证是否支持CommonJS/ES6Module规范(支持这个规范的是Node.js)
  29. if ( typeof module === "object" && typeof module.exports === "object" ) {
  30. // For CommonJS and CommonJS-like environments where a proper `window`
  31. // is present, execute the factory and get jQuery.
  32. // For environments that do not have a `window` with a `document`
  33. // (such as Node.js), expose a factory as module.exports.
  34. // This accentuates the need for the creation of a real `window`.
  35. // e.g. var jQuery = require("jquery")(window);
  36. // See ticket #14549 for more info.
  37. // 代码是运行在node环境下的(或者是基于webpack打包运行的项目)
  38. module.exports = global.document ?
  39. factory( global, true ) :
  40. function( w ) {
  41. if ( !w.document ) {
  42. throw new Error( "jQuery requires a window with a document" );
  43. }
  44. return factory( w );
  45. };
  46. } else {
  47. // 运行在浏览器或者webview(相当于浏览器)中
  48. // =>B(window)
  49. factory( global );
  50. }
  51. })(A, B);

应用

在我们自己编写类库/插件/UI组件/框架的时候,为了防止全局变量污染,我们需要基于闭包的机制进行”私有化“处理。

封装一个轮播路banner插件

  1. (function(){
  2. function Banner(){}
  3. window.Banner = Banner;
  4. })()
  • 能够在浏览器中运行
  • 能支持CommonJS或者ES6Module规范(node/webpack)
    1. (function(){
    2. function Banner(){}
    3. // 浏览器环境
    4. if(window!==undefined) {
    5. window.Banner = Banner;
    6. }
    7. // 支持CommonJS或者ES6Module
    8. //if(typeof module !== "undefined" && typeof module.exports !== "undefined") {
    9. if ( typeof module === "object" && typeof module.exports === "object" ) {
    10. module.exports = Banner;
    11. }
    12. })()