首页 > 编程语言 >JS宏进阶:模块化编程

JS宏进阶:模块化编程

时间:2025-01-23 11:27:40浏览次数:3  
标签:function ... return 进阶 模块化 module1 JS 模块 var

理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。然而,理想很丰满,现实很骨感,特别是在WPS编辑器中,加载别人写好的模块需要用eval方法。很不安全,WPS官方又没有开发出独特的加载方式,下面是一个模块的加载示例:

var CryptoJS; //.MD5(inputString).toString()
var _;


async function cryptoJs(){
	let str = await fetch("https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js").then(x => x.text());
	eval(str);
}


async function lodash(){
	const str = await fetch("https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js").then(x => x.text());
	eval(str);
}


async function MD5Test(){
	await cryptoJs(); //引入前端加密模块crypto-js.min.js
	print(CryptoJS.MD5("123").toString());
	await lodash(); //引入前端模块lodash
	const array = [1, 2, 3, 4, 5];
	const chunked = _.chunk(array, 2);
	console.log(JSON.stringify(chunked));
}

效果图:

这是执行三次后的结果,可以发现,有时某些模块并没有成功的加载进来。经过UP多次测试,发现,当同时加载多个模块时,部分模块无法成功加载。很不好用,而且要还是使用eval,是一种不安全的行为。因此,在wps编辑器中,程序员开发自己的模块成了一个迫切的需求。

在以往的文章中,已经介绍过面向对象的编程思路。模块化,也是面向对象编程的一种,也就是说之前我们定义的构造函数、class类本身就是一种简单的模块化编程方式。在本文中,将对这种模块化的编程方式进行详细介绍。

一、原始写法

模块就是实现特定功能的一组方法。只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。

例如:

function myModule1(){
    //定义你的方法
}

function myModule2(){
    //定义你的方法
}

上面,myModule1 与 myModule2 两个函数,放在一起,就构成了一个模块

二、对象写法

就是之前文章中提到的使用new Object( )或{ }来定义一个对象,在其中定义自己的属性和方法。例如:

var module1 = new Object({
   _count : 0,
   mod1 : function (){
    //...
   },
   mod2 : function (){
     //...
   }
});

在上述示例中,将mod1和mod2两个方法都封装在module1对象里。使用的时候,就是调用这个对象的属性。并且定义了一个私有变量_count。然而,这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。不是很安全。

三、使用构造函数封装私有变量

function mod() {
    var buffer = [];

    this.add = function (str) {
        buffer.push(str);
    };

    this.toString = function () {
        return buffer.join('');
    };

}

这种方法虽然将私有变量封装在构造函数中,但是违反了构造函数与实例对象相分离的原则。并且,非常耗费内存。不是很推荐。有人可能会说,我可以这样:

function mod() {
    this._buffer = [];
}

mod.prototype = {
    constructor: mod,
    add: function (str) {
        this._buffer.push(str);
    },
    toString: function () {
        return this._buffer.join('');
    }
};

然而,如果这样编写,_buffer又不是真正意义上的私有变量了。在外部依旧可以从外部进行读写,不太安全。

四、IIFE封装模块的方法与属性(本文重点)

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

var module1 = (function(){
    var _count = 0;
    var setCount = function(count){
        _count = count;
    }
    
    var getCount = () => _count;
    return {
        setCount : setCount,
        getCount : getCount
    }
})();

效果图如下:

由于立即函数表达式的返回值中,没有直接返回_count,所以,外部无法直接访问它,除非调用对应的方法进行访问和修改。

1、放大模式

var module1 = (function (mod = {}){
 mod.f = function () {
  //...
 };
 return mod;
})(module1);

如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用"放大模式",上述示例中,为module1模块添加了一个新方法f,并返回新的module1。

2、输入全局变量

var module1 = (function (math, array) {
    // 私有变量和函数
    var pi = math.PI;
    
    function getRandomInt(min, max) {
        // 使用math.floor和math.random生成一个随机整数
        return math.floor(math.random() * (max - min + 1)) + min;
    }

    // 公有API
    return {
        calculateCircumference: function (radius) {
            // 计算圆的周长
            return 2 * pi * radius;
        },

        createArrayWithRange: function (start, end) {
            // 创建一个包含从start到end的数组
            var result = [];
            for (var i = start; i <= end; i++) {
                result.push(i);
            }
            return result;
        },

        shuffleArray: function (arr) {
            // 使用Fisher-Yates洗牌算法打乱数组
            for (var i = arr.length - 1; i > 0; i--) {
                var j = getRandomInt(0, i);
                // 交换元素
                var temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
            return arr;
        },

        printArrayInfo: function (arr) {
            // 打印数组信息
            console.log("Array length: " + arr.length);
            console.log("Array elements: " + arr.join(", "));
        }
    };
})(Math, Array);

function main(){
	// 使用module1的公有API
	var circumference = module1.calculateCircumference(5);
	console.log("Circumference of a circle with radius 5: " + circumference); // 输出: Circumference of a circle with radius 5: 31.41592653589793
	
	var rangeArray = module1.createArrayWithRange(1, 10);
	console.log("Array with range 1 to 10: ", rangeArray); // 输出: Array with range 1 to 10:  [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
	
	var shuffledArray = module1.shuffleArray([1, 2, 3, 4, 5]);
	console.log("Shuffled array: ", shuffledArray); // 输出: Shuffled array:  [ ... ] (随机顺序)
	
	module1.printArrayInfo(shuffledArray); // 输出数组的长度和元素
}

独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。为了在模块内部调用全局变量,必须显式地将其他变量输入模块。上述示例中,就是将全局的Math,Array对象当作了module1的输入参数,这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显。执行效果图如下所示:

3、常规封装结构

var myModule = function() {  // open IIFE

    // public (公有)
    var self = {
        publicMethod: function (...) {
            privateData = ...;
            privateFunction(...);
        },
        publicData: ...
    };

    // private (私有)
    var privateData = ...;
    function privateFunction(...) {
        privateData = ...;
        self.publicData = ...;
        self.publicMethod(...);
    }

    return self;
}(); // close IIFE

五、总结

总的来说,JavaScript 模块化编程是一种将代码分割成独立、可复用的模块的方法。这种方法有助于提高代码的可维护性、可读性和可重用性。模块化编程的核心思想是将功能相关的代码组织在一起,而将无关的代码分离,从而降低代码的复杂性。在WPS编辑器中,模块化可以通过多种方式实现,其中最为推荐的是IIFE的写法,因为这种写法作用域完全封闭,比较安全。当然,调用Symbol方法,在ES6类中,也可以实现私有属性:

const privateKey = Symbol('privateKey');

class MyClass {
    constructor(value) {
        this[privateKey] = value; // 私有变量
    }

    // 公有方法
    getValue() {
        return this[privateKey];
    }
}

const instance = new MyClass(42);
console.log(instance.getValue()); // 输出: 42
console.log(instance[privateKey]); // undefined,因为 Symbol 是唯一的,外部无法访问

但是,该方法比较复杂,需要为每个私有变量创建一个Symbol,而且,在JSON序列化时,会被忽略,虽然可以实现,但也不如IIFE来的方便快捷。

标签:function,...,return,进阶,模块化,module1,JS,模块,var
From: https://blog.csdn.net/jackispy/article/details/145297897

相关文章

  • JSP农副及衍生产品交易系统3qxs1--(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义随着互联网技术的发展,电子商务已成为推动各行各业数字化转型的重要力量。在农副产品领域,传统的交易模式存在信息不对称、交易效......
  • JSP农业种植管理系统5kb33--(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、项目背景与意义随着科技的飞速发展,农业领域正逐步向智能化、信息化转型。农业种植管理作为农业生产的关键环节,其效率与质量直接影响农作物的产......
  • 在 CentOS 7 上为 Jenkins 配置 Node.js 和 npm 的完整指南
    个人名片......
  • js 数组 push() pop() shift() unshift() splice() sort() reverse() 的这些方法的用
    JavaScript的数组对象提供了多种方法来操作数组内容。以下是您提到的方法的简要说明和用法:1.`push()`-用途:向数组的末尾添加一个或多个元素,并返回新的长度。-语法:`array.push(element1,...,elementN)`-示例:`letarr=[1,2,3];arr.push(4);//arrisnow[1,......
  • JS逆向和前端加密暴力破解(小白无痛学习),黑客技术零基础入门到精通教程!
    网站运行的时间轴url–>加载html–>加载js–>运行js初始化–>用户触发某个事件–调用了某段js–>明文数据–>加密函数–>加密后的数据–>send(给服务器发信息{XHR–SEND})-->接收到服务器数据–>解密函数–>刷新函数–>刷新网页渲染浏览器的调试功能调试时使用最多的功......
  • uni-app+egg.js实战直播app全栈开发
    uni-app实战直播app全栈开发1.课程介绍(买前必看).mp42h-9-4h18-uni-app+egg.js实战直播app全栈开发·node.js直播服务器搭建socket.io实时送礼物、实时弹幕功能·全栈开发兼容Android、ioS、小程序等9.登录注册页开发1.项目介绍10.个人中心页开发2.环境搭建和项目创建11.eg......
  • JS-Web API -day04
    一、日期对象1.1实例化日期对象实例化:new关键字获得当前时间        constdata=newDate()获得指定时间        constdata1=newDate('2024-5-108:30:00')     1.2日期对象方法常见的时期对象方法:getFullYear()、getMonth()、g......
  • 洛谷题单指南-线段树的进阶用法-P4602 [CTSC2018] 混合果汁
    原题链接:https://www.luogu.com.cn/problem/P4602题意解读:在一堆果汁中选出若干果汁,使得最小的美味度最大,且总体价格小于等于g,总体体积大于L,求这个最大的美味度。解题思路:显然,应该对答案进行二分,二分到一个美味度x,那么接下来check()函数要做的事,就是在所有美味度>=x的果汁中,查......
  • hutool工具JSONUtil序列化对象和反序列化到Map的时候,null的值因为JSONNull无法转换而
    importcn.hutool.json.JSONNull;importcom.fasterxml.jackson.core.JsonGenerator;importcom.fasterxml.jackson.databind.JsonSerializer;importcom.fasterxml.jackson.databind.SerializerProvider;importorg.springframework.boot.jackson.JsonComponent;import......
  • 挣脱代码枷锁,转型项目经理的进阶之路
    前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦......