小试基于JavaScript的面向对象思想实践

本文讲述了作者对JavaScript面向对象思想的实践经历,从最初的轻视到深入理解,通过设计模式的书籍,了解到JavaScript中的类、原型继承和面向对象特性。作者尝试用JavaScript实现一个云主机管理系统,挑战了原生JavaScript的读写文件、加密等功能,以此来探讨面向对象在现代开发中的地位。
摘要由CSDN通过智能技术生成

经常看到“面向对象已死”、“面向对象被弃用”、“面向对象已过时”等等论调,作为开发大军里的一枚边际小棋子,实在不敢妄断,也不清楚前沿大佬是怎么看待、分哪几个派别。不过,目前来看,J2EE占主流的应用开发市场,到处充斥着类和对象的定义。——这一定义是在大学时期学习面向对象思想(C++)课程时接触到的。上了大半学期,老师——万琳,穿绿色衣服的,如巧遇同门希望能点赞示意:-)——说过一句话,这门课主要是讲述面向对象思想,不是Windows编程,那是另外一门课。说实话,当时一下子懵了:学了半天学了个寂寞,这不是编程啊,是披着windows外衣的C++语言?

学完谭浩强的经典C语言编程书籍,再学C++是不是意味着我们的水平进一步提升了?还是学校觉得我们的水平提高了,可以接触更难的C++了?——当时就是这么想的,汇编这种低开发效率、晦涩难懂、“过时”的语言,学校教了个皮毛,学习了个云山雾绕;学完C语言,大体能写点什么了,但总感觉档次太低,水平有限,只能在命令行环境运行;总认为哪些写窗体的才是大牛——你看,人家做的界面跟windows的主题风格一致,也符合用户的操作习惯,这才是编程!

当然,自从被罗云彬拐跑以后,再也没怎么碰过界面开发呵呵,这是后话。彼时,只认为编程必讲类、对象、继承、多态、封装......,高级语言嘛,讲这个才高级、内行、上档次!只要有需求,先建一个xx类,添加一堆函数,按需添加几个变量,一般都是共有的呵呵,相当开放,不会有错。最后实例化对象,和其它对象协作运行程序,似乎面向对象思想也不过如此呵呵。但仍有一丝丝疑虑:这就是oo?升级版的结构、联合和printf函数?也有人对之一竿子打翻:只不过是套了个面向对象的壳子!嗯,从此疑窦丛生,深以为然。没觉得它比面向过程的C语言高级到哪里去,除了“cout、cin”比“printf”更简洁一些以外。不过也没多想,也许开发的世界里大抵如是吧。至少“类”这种抽象的思想已经植根于脑海了,也算了解了oo的基础。相信大家都被普及过银行与用户的例子,这是很典型的类与对象的现实例子。

这还是有实际意义的:后来接触C#、Java等都觉得十分容易上手,不存在什么入门、转型门槛了,几乎拿个例子来看看就能照猫画虎。可以说“面向搜索引擎”编程已经是水到渠成。O(∩_∩)O~这让开发成了体力活、青春饭。那么何去何从?

另外,虽然因项目的原因对JavaScript也有所了解,不过始终认为它只是用于辅助前端展示的小工具,根本不能和C/C++、Java等相提并论,更不屑于去投入精力深入了解。连定义个变量都只用“var”,根本没有“int、float、char”等这种类别,人称其“弱类型”语言。想想这也够简单,如同玩具。多年来回头看,发觉这可能也是它的长处:这就不存在类型匹配、类型转换的问题了(当然JavaScript中仍存在强制类型转换,但不是同一概念)。这也有时代的原因,当时的JavaScript远没有现在这么强大,也就是用于提交一下表单等。

直到有一天,遇到了一堆不明其故的“乱码”——$("#div").html("value").....

如果没有接触过jQuery的话,估计是看不懂这是在干吗。这个风格一改往日JavaScript的面貌,令人耳目一新。本以为这么复杂的“符号式”程序代码会很难,但是发现它的理念相当舒适:“write less,do more”。这不是我们梦寐以求的吗?于是花了一点时间学习了一下,突然发现它的学习成本很低,一篇w3cshool,居然能让人得心应手地把jQuery玩出花来。从此开始重视前端了,原来JavaScript还可以这么玩!感觉jQuery精华是其本身源码,而不是它的衍生物。第一次对JavaScript对象,prototype原型等有了初步了解,不得不感叹作者的实力与思想,远非我等可以望其项背的。可能先入为主吧,以后对React和Vue就没那么狂热了。

从此与前端结缘......

仅仅结缘而已,真正有过面向对象O-O体会的,还得从一本书说起。书名忘了,是关于设计模式的JavaScript实现的书,肯定是电子版,为了解决生活空间和搜索效率的问题^_^。副作用就是,硬盘被人为损坏后,再也想不起它的书名了( ˇˍˇ )。书中用JavaScript,不是jQuery,实现了工厂模式、单体模式、适配器模式、命令模式等等各式各样的模式。书中的实例都采用面向对象的方式,定义类、对象等,并通过继承来验证一些功能等。哪怕需要一个div标签、input标签,作者也是通过document.createElement()来创建,不用html静态标签。为什么作者用JavaScript,我一直有点疑惑。按理说用C++或Java会更大众化。不过正是这个疑惑,让我重新认识了JavaScript:

1、function不一定是函数,还可以是类(ES6以后有了class关键字)

2、JavaScript类的继承方式是原型继承,继承自原型链

3、创建对象使用new关键字,对象均继承自JavaScript对象Object,它是常量

4、好像还有个constructor构造函数,没用过,一般直接在用类定义对象的时候初始化

5、真正认识了this指针,也得益于对jQuery源码的分析

6、每个类都有一个隐藏属性:prototype,以前叫__proto__

这些大多都超出了的当时的认知水平,算是开眼界了。

转眼nnnn年过去了,ES6已不再是新宠,35岁门槛已成为行规,JavaScript的框架愈来愈多,j2ee占据半壁江山,面向对象已成鸡肋的说法甚嚣尘上,coding被鄙视papering(ppting)被颂扬......在工作中突然自行挖掘出一个管理云主机的需求:记录使用的云主机名、占用资源、账号密码、启用时间等。本来一个Excel表可以搞定,可是表越做越大,还得兼顾保密要求,时不时来个分类统计。

某一次灵光咋现,突然想到何不做一个简单系统?用于专门记录这些数据,并且可以自由选择可靠的加密方式,这保险系数和使用方式独一无二,既满足安全性还能自行按需进行二次开发调整。事实证明,可行,但不建议。当时想简单了,本以为开发一个简易的、用于记录简单数据的程序或表单,很简单。但是忽略了一个事实:任何用来记录数据或者数据库程序,至少要实现“CRUD”增删改查,随随便便一个内容的展示,都要至少匹配这四样功能(;′⌒`)。所以,看是简易,工作量还是不小,尤其改bug。庆幸,当时是满腔热血,一往直前,披荆斩棘,最后伤痕累累。工作成果可以认为是尝试探索,也可以看做是祭旗,就看主观立场和角度。也很庆幸,坚持下来了,有所收获,并不觉得亏。希望能对阅览者有所帮助,至少少走弯路,能够对面向对象是否已故给一个评价。

理一下需求:

1、云主机需要分类:如财务系统、智能分析系统、OA系统等

2、云主机要记录:CPU、内存、存储、账号、密码等信息

3、可总览或按分类查看云主机数量和信息

4、需要按分类统计计算资源(CPU、内存等)的使用量

5、操作维护简单

6、对第三方软件、插件依赖越少越好

就这么简单!

也许有人会说,access数据库+eclipse或VS,或者不用数据库直接把Excel文档当做数据源来做,太基础了。如果这样,还有必要讨论面向对象吗?

面对仍在服役的、使用奔腾(R)处理器的办公电脑,我确实不舍得安装开发环境^_^,怕它太累。于是想到了那本基于JavaScript的关于设计模式的书,默默地打开了记事本......

没错,第一次尝试着从面向对象的角度来设计一个软件:

1、所有云主机抽象为一个类,包含CPU、内存等属性,主机对象是类的一个对象或实例

2、所有云主机分类抽象一个类,包含分类名称,云主机明细等属性,分类为该类的一个对象或实例

3、分类对象可以增加、删除、修改、查询本分类的所有云主机

4、分类对象可以自动计算本分类指定资源的总量

5、文本文件作为数据源,便于移植和操作

6、文本文件内容可以被加密,使用AES加密方式(本来想使用RSA非对称加密,研究了半天突然醒悟到它不是做这个的)

7、尽量、尽量、尽量不使用第三方插件、中间件

8、坚持尝试使用原生JavaScript,可以免去编译操作

也许大神们会嗤之以鼻:就这?对,就这,这是我认知范围内,对面向对象思想的理解了。

html页面代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script src="crypto-js.min.js"></script>
    <style type="text/css">

        table.gridtable {

            font-family: verdana,arial,sans-serif;

            font-size:11px;

            color:#333333;

            border-width: 1px;

            border-color: #666666;

            border-collapse: collapse;

        }

        table.gridtable th {

            border-width: 1px;

            padding: 8px;

            border-style: solid;

            border-color: #666666;

            background-color: #dedede;

        }

        table.gridtable td {

            border-width: 1px;

            padding: 8px;

            border-style: solid;

            border-color: #666666;

            background-color: #ffffff;

        }

    </style>
</head>
<body>

    <table class="gridtable" id="tbl">

        <tr><td><input id='pwd' type="password" value="pswd"/></td><td><input type="button" onclick="login()" value="显示"/></td><td><a id="de">下载解密文件</a></td><td><a id="en">下载加密文件</a></td> </tr>

    </table>


<script type="text/javascript" src="serverViewjs.js"></script>
<script type="text/javascript" src="serverGroupjs.js"></script>
<script type="text/javascript" src="serverControllerjs.js"></script>
<script type="text/javascript" src="globlejs.js"></script>
<script>



	throw new Error('this is my customed error');
</script>
</body>
</html>

用到了一个第三方库:crypto-js.min.js,主要用于加密解密序列化后的对象,还有一个aes.js的库,回头试试看能不能用它替换掉这个库。其它的js文件就是实现:

1、serverViewjs.js用于前端展示,这个脚本的开发工作量最大

2、serverGroupjs.js定义和实现分类和云主机两个类

3、serverControllerjs.js定义的控制类,用于实现对分类和云主机两个类对象的操作
4、globlejs.js定义一些全局函数和对象

serverViewjs.js源码如下:



function ServerView(objController){
	if(objController==null){
		this.controller=gObjServerController;
	}else{
		this.controller=objController;
	}
	
	this.objTitle=(this.controller.objTitle==null?thedetail:this.controller.objTitle);
}

ServerView.prototype={
	ul:{},
	table:{},
	div:{},
	mainDiv:{},
	groupDiv:{},
	serverDiv:{},
	editDiv:{},
	totalDiv:{},
	controller:{},
	selectedLiValue:null,
	selectedTrObj:null,
	currentLiValue:null,
	currentTrObj:null,
	init:function(){
		this.ul=document.createElement("ul");
		this.ul.style="list-style-type:none;";

		this.mainDiv=document.createElement("div");
		this.mainDiv.id="mDiv";
		this.mainDiv.style="border:solid 1px #18a;display:block;";

		this.groupDiv=document.createElement("div");
		this.groupDiv.style="border:solid 0px #c99;width:30%;display:block;float:left;";
		this.groupDiv.append(this.ul);

		this.editDiv=document.createElement("div");
		this.totalDiv=document.createElement("div");

		this.editDiv2=document.createElement("div");
		this.totalDiv.style="border:solid 1px #ccc;display:block;";
		var divs=document.createElement("div");

		var divclear=document.createElement("div");
		divclear.style="clear:both;";
		
		divs.style="border:solid 0px #ccc;width:70%;float:left;";
		
		divs.append(this.editDiv);
		divs.append(this.editDiv2);
		divs.append(this.totalDiv);
		this.serverDiv=document.createElement("div");

		this.initBtnSaveGroup();
		this.initNewServerTable();
		this.initTable();
		this.refreshTotal(null);
		
		this.mainDiv.append(this.groupDiv);
		this.mainDiv.append(divs);
		this.mainDiv.append(divclear);
		this.mainDiv.append(this.serverDiv);
		document.body.append(this.mainDiv);

	},
	initTable:function(){
		var div=this.serverDiv;
		var that=this;

		this.table=document.createElement("table"); 
		this.table.setAttribute("class","gridtable");

		var tr1=document.createElement("tr");
		var tr2=document.createElement("tr");
		for(var columncaption in this.objTitle){
			var th=document.createElement("th");
			var txt=document.createTextNode(this.objTitle[columncaption]);
			th.append(txt);
			tr1.append(th);

		}
		this.table.append(tr1);

		div.append(this.table);
	},
	initBtnSaveGroup:function(){
		var that=this;
		var newGroup=document.createElement("input");
		newGroup.type="text";
		newGroup.id="newGroupObj";

		var newGroupName=document.createElement("input");
		newGroupName.type="text";
		newGroupName.id="newGroupName";
				
		var addNewGroup =document.createElement("input");
		addNewGroup.type="button";
		addNewGroup.value="新建系统分类";

		addNewGroup.onclick=function(){
			var retGroups=that.controller.addGroup(newGroup.value,newGroupName.value);
			newGroup.value="";
			newGroupName.value="";
			that.refreshUL(retGroups);
		};

		var delGroup =document.createElement("input");
		delGroup.type="button";
		delGroup.value="删除系统分类";

		delGroup.onclick=function(){
			var retGroups=that.controller.deleteGroup(newGroup.value);
			newGroup.value="";
			newGroupName.value="";
			that.refreshUL(retGroups);
		};

		this.editDiv.append(newGroup);
		this.editDiv.append(newGroupName);
		this.editDiv.append(addNewGroup);
		this.editDiv.append(delGroup);
	},
	initNewServerTable:function(){
		var div=this.editDiv2;
		var that=this;

		this.newServertable=document.createElement("table"); 
		this.newServertable.setAttribute("class","gridtable");

		var tr1=document.createElement("tr");
		var tr2=document.createElement("tr");
		for(var columncaption in this.objTitle){
			var th=document.createElement("th");
			var txt=document.createTextNode(this.objTitle[columncaption]);
			th.append(txt);
			tr1.append(th);


			var td=document.createElement("td");
//			var col=Object.keys(columns[columncaption])[0];
			var col=columncaption;

			switch(col){
				case "account":
				case "pwd":
					var tArea=document.createElement("textarea");
					tArea.id="_"+col;
					td.append(tArea);
					break;	
				case "operation":
						var btnSaveServer=document.createElement("input");
						btnSaveServer.value="保存";
						btnSaveServer.type="button";
						btnSaveServer.id=this.mainDiv.id;

						btnSaveServer.addEventListener("click", function(){
							if(that.selectedLiValue==null){
								console.log(this);return;
							}
							var objServer;
						
							
							if(that.controller==gObjServerController){
								objServer=new Server();
								var serverId=that.currentTrObj;
								if(serverId==null){
									serverId=that.selectedLiValue+(new Date()).getTime();
								}
								for(var eachItem in that.objTitle){
									objServer[eachItem]=document.getElementById("_"+eachItem).value;
								}

								var originGroup=that.currentLiValue;
								var newGroup=that.selectedLiValue;alert(serverId);
								var retGroup=that.controller.updateServer(serverId,objServer,originGroup,newGroup);
								that.currentTrObj=null;
								that.clearTable(that.table,1);
								that.refreshTable(retGroup.detail,that.objTitle);
							}else{alert();}
							btnReset.click();
						});

						td.append(btnSaveServer);

						var btnReset=document.createElement("input");
						btnReset.value="重置";
						btnReset.type="button";

						btnReset.addEventListener("click", function(){
							for(var eachItem in that.objTitle){
								document.getElementById("_"+eachItem).value="";
							}
							that.currentLiValue=null;
							that.currentTrObj=null;
						});

						td.append(btnReset);

				default:
					var inputTxt=document.createElement("input");
					inputTxt.type="text";
					inputTxt.id="_"+col;
					inputTxt.alt="";	
					td.append(inputTxt);
			}
			tr2.append(td);
		}
		this.newServertable.append(tr1);
		this.newServertable.append(tr2);

		div.append(this.newServertable);
	},
	clearDiv:function(objDiv){
		objDiv={};
	},
	refreshUL:function(obj){
		var that=this;
		while(this.ul.hasChildNodes()){
			this.ul.removeChild(this.ul.firstChild);
		}
		for(eachGroup in obj){
			var li=document.createElement("li");
			var liTxt=document.createTextNode(obj[eachGroup].gpname);
			var liRd=document.createElement("input");
			liRd.type="radio";
			liRd.name="GroupName";
			liRd.value=eachGroup;

			(function(thisli,rd){
				thisli.addEventListener("click",function(){
					rd.checked=true;
					that.selectedLiValue=rd.value;
					that.clearTable(that.table,1);
					var serverGroup;
					var cpuNum,memNum,storageNum;
					if(that.controller==gObjServerController){
						serverGroup=that.controller.getServerGroup(rd.value);
						cpuNum=that.controller.sumResource(serverGroup,"CPU");
						memNum=that.controller.sumResource(serverGroup,"mem");
						storageNum=that.controller.sumResource(serverGroup,"storage");
					}
					that.refreshTable(serverGroup.detail,that.objTitle,that.table);

					while(that.totalDiv.hasChildNodes()){
						that.totalDiv.removeChild(that.totalDiv.firstChild);
					}
					that.appendToTotalDiv("CPU",cpuNum);
					that.appendToTotalDiv("内存",memNum);
					that.appendToTotalDiv("存储",storageNum);
				});

			})(li,liRd);

			li.style.cursor="pointer";
			li.append(liRd);
			li.append(liTxt);
			this.ul.append(li);
		}
		this.selectedLiValue=null;
	},
	clearTable:function(table,beginRow){
		if(beginRow==null){
			beginRow=1;
		}
		while(table.rows.length>beginRow){
			table.deleteRow(beginRow);
		}
	},
	refreshTable:function(serverGroup,objTitle,table){
		if(table==null){
			table=this.table;
		}
		var vid=table.rows.length;

		for(var eachServer in serverGroup){
			serverGroup[eachServer].vid=vid++;
			this.selectedTrObj=eachServer;
			this.appendToTable(serverGroup[eachServer],objTitle,table);
		}

		this.selectedTrObj=null;
	},

	appendToTable:function(server,objTitle,tbl){
		var that=this;
		var tr=document.createElement("tr");

		for(var columncaption in this.objTitle){
			var td=document.createElement("td");
			var serverid =	this.selectedTrObj;
			var groupid=this.selectedLiValue;
			if(columncaption=="operation"){
				var btnUpdate=document.createElement("input");
				btnUpdate.type="button";
				btnUpdate.value="修改";
				btnUpdate.addEventListener("click", function(){
					that.currentLiValue=groupid;
					that.currentTrObj=serverid;
					for(var eachItem in that.objTitle){
						document.getElementById("_"+eachItem).value=server[eachItem];
					}
				});
				td.append(btnUpdate);
				var controller;
				if(that.controller==gObjServerController){
						controller=gObjServerController;
						var btnDel=document.createElement("input");
						btnDel.value="删除";
						btnDel.type="button";
						btnDel.addEventListener("click", function(){
							that.currentTrObj=null;
							that.currentLiValue=null;
							var controller=gObjServerController;
							var retGroup=controller.delServerFromGroup(serverid,groupid);
								
							that.clearTable(that.table,1);
							that.refreshTable(retGroup.detail,that.objTitle);
						});
				}else{}
				td.append(btnDel);

			}else{
				var txt=document.createTextNode(server[columncaption]);
				td.append(txt);
			}
			tr.append(td);
		}
		tr.addEventListener("dblclick", function(){
			that.currentLiValue=groupid;
			that.currentTrObj=serverid;
			for(var eachItem in server){
				document.getElementById("_"+eachItem).value=server[eachItem];
			}
		});
		tr.style.cursor="pointer";
		tbl.append(tr);
	},

	refreshTotal:function(serverGroups){
					while(this.totalDiv.hasChildNodes()){
						this.totalDiv.removeChild(this.totalDiv.firstChild);
					}
		var cpuNum=0,memNum=0,storageNum=0;
		if(this.controller==gObjServerController){
			cpuNum+=this.controller.sumGroups(serverGroups,"CPU");
			memNum+=this.controller.sumGroups(serverGroups,"mem");
			storageNum+=this.controller.sumGroups(serverGroups,"storage");
		}
		this.appendToTotalDiv("CPU",cpuNum);
		this.appendToTotalDiv("内存",memNum);
		this.appendToTotalDiv("存储",storageNum);
	},
	appendToTotalDiv:function(resource,value){
		var totalnode=document.createTextNode(resource +" total :"+ value);
	///	document.body.append(totalnode);
		this.totalDiv.append(totalnode);
	}
};

serverGroupjs.js源码如下:

function ServerGroup(){

}


function Server(){
	for(var eachItem in thedetail){
		this[eachItem]=thedetail[eachItem];
	}
}



Server.prototype={
	
};

serverControllerjs.js源码如下:


function ServerController(objData){
	if(objData==null){
		this.localObj=obj0;
	}else{
		this.localObj=objData;
	}
	this.objTitle=thedetail;
	console.log(this);console.log(obj0==this.localObj);
}

ServerController.prototype.getServerGroup=function(typevalue){
	return this.localObj[typevalue];
};

ServerController.prototype.updateServer=function(serverId,server,originGroup,newGroup){
	var group=this.addServerToGroup(server,serverId,this.localObj[newGroup]);
	if(originGroup==null||originGroup==""||originGroup==newGroup){
		
	}else{
		this.delServerFromGroup(serverId,originGroup);
	}

	return group;
};

ServerController.prototype.addServerToGroup=function(s,serverName,g){
	g.detail[serverName]=s;
	return g;
};

ServerController.prototype.delServerFromGroup=function(sid,gid){
	var groups=this.localObj;
	delete groups[gid].detail[sid];
	return groups[gid];
};

ServerController.prototype.addGroup=function(gid,gcaption,gs){
	var groups=(gs==null?this.localObj:gs);
	if(groups[gid]==null){
		groups[gid]={};
		groups[gid].detail={};
	}
	groups[gid].gpname=gcaption;
	return groups;
};

ServerController.prototype.deleteGroup=function(gid,gs){

//	delete g;
	var groups=(gs==null?this.localObj:gs);
	delete groups[gid];console.log("deleted");
	return groups;
};

ServerController.prototype.sumResource=function(group,resource){
	var total=0;
	for(var eachItem in group.detail){
		total+=(group.detail[eachItem][resource]==null?0:parseInt(group.detail[eachItem][resource]));
	}
	return total;
};

ServerController.prototype.sumGroups=function(groups,resource){
	var total=0;var allGroups=null;
	if(groups==null){
		allGroups=this.localObj;
	}else{
		allGroups=groups;
	}
	for(var eachItem in allGroups){
		total+=this.sumResource(allGroups[eachItem],resource);
	}
	return total;
};

globaljs.js源码如下:


var encrypted = CryptoJS.AES.encrypt("你好",'pswd');

var decrypted = CryptoJS.AES.decrypt(encrypted ,'pswd').toString(CryptoJS.enc.Utf8);


//var filepath="aes.js";
function load(name) {

 let xhr = new XMLHttpRequest(),

    okStatus = document.location.protocol === "file:" ? 0 : 200;


//	okStatus = document.location.protocol === "file:" ? 200 : 200;//新版本200?

    xhr.open('GET', name, false);

    xhr.overrideMimeType("text/html;charset=utf-8");//默认为utf-8

    xhr.send(null);

    return xhr.status === okStatus ? xhr.responseText : null;

}


var obj0;
var gObjServerController=null;
var gObjServerView=null;
var thedetail={"vid":"序号","type":"业务系统","caption":"服务器名称","OS":"操作系统","ip":"内网ip","NAT":"外网映射","account":"账号","pwd":"密码","CPU":"CPU","mem":"内存","storage":"存储","more":"备注","operation":"操作"};

//thedetail={"vid":"序号","type":"业务系统","account":"账号","pwd":"密码","more":"备注","operation":"操作"};

function login(){
	var resultOfcheck=checkPwd(document.getElementById('pwd').value);
	if(resultOfcheck){
		if(gObjServerView==null){
			gObjServerController=new ServerController();
			gObjServerView=new ServerView();
			gObjServerView.init(gObjServerController);
		}

		if(gObjServerController.localObj!=null){
			obj0=gObjServerController.localObj;
			setDownLoad(gObjServerController.localObj);
		}
		gObjServerView.clearTable(gObjServerView.table,1);
		for(var eachItem in gObjServerController.localObj){
			gObjServerView.selectedLiValue=eachItem;
			gObjServerView.refreshTable(gObjServerController.localObj[eachItem].detail,thedetail);
		}
		gObjServerView.refreshUL(gObjServerController.localObj);
		gObjServerView.refreshTotal(null);
	}else{alert("wrong password!");}
}

function checkPwd(pwdKey){

	var decryptText,encryptText;
	try{
		encryptText = load("encryptData.txt");
	

	if(pwdKey==""){
		decryptText=encryptText ;
	}else{
		decryptText = CryptoJS.AES.decrypt(encryptText ,pwdKey).toString(CryptoJS.enc.Utf8);
	}

	obj0 = JSON.parse(decryptText);

	return true;
	}catch(e){
		console.log(e);
		return false;
	}
}

function setDownLoad(jsonObj){
	var txtName="下载解密文件";
        var save_link = document.getElementById("de");//document.createElementNS("http://www.w3.org/1999/xhtml", "a");
        save_link.download = "decrypt";
	save_link.href="";
	save_link.onclick=function(){
		updateDownloadLink(this,JSONToTxt(jsonObj));
	};

        var save_link2 = document.getElementById("en");
        save_link2.download = "encryptData";
	save_link2.href="";
	save_link2.onclick=function(){
		pwdKey=document.getElementById('pwd').value;
		var endata=JSONToTxt(jsonObj); 
		updateDownloadLink(this, CryptoJS.AES.encrypt(endata,pwdKey));
	};
};

function JSONToTxt(obj){
//	console.log(JSON.stringify(obj0));//序列化
	var retVal=null;
	try{
		retVal=JSON.stringify(obj)
	}catch(e){}

	endata= CryptoJS.AES.encrypt(retVal,'pswd');
	return retVal;
}

function updateDownloadLink(objLink,data){
	var urlObject = window.URL || window.webkitURL || window;
        var export_blob = new Blob([data]);
	objLink.href = urlObject.createObjectURL(export_blob);
}

其中有几点需要注意:

1、为了减少对第三方软件或插件依赖,使用JavaScript读取txt文件、保存数据到文件,省去了后端代码的(如nodejs、Apache等)开发与部署步骤

2、没找到直接保存数据到数据源(txt文件)的方式(求助~~~求回复~~~),采用了下载数据文件的折中办法

3、数据修改后需要下载加密或解密文件(encryptData.txt或decrypt.txt)替换原文件,以保存修改内容

4、以上方式仅在旧版本的chrome浏览器有效,实验版本为chrome87.0.4280.88,需要添加“ --allow-file-access-from-files“参数(可在浏览器快捷方式的“属性-快捷方式-目标”里添加,或者用命令行启动),新版本chrome如106+,为了保证安全,关闭了该功能接口,遗憾啊,谷歌越来越封闭了!~~~~(>_<)~~~~

还有一个crypto-js.min.js不知道怎么作为附件传上来。改天试一试能不能用aes.js替代。

aes.js源码如下(非原创,与crypto-js.min.js同源):

(function () {
    // Shortcuts
    var C = CryptoJS;
    var C_lib = C.lib;
    var BlockCipher = C_lib.BlockCipher;
    var C_algo = C.algo;

    // Lookup tables
    var SBOX = [];
    var INV_SBOX = [];
    var SUB_MIX_0 = [];
    var SUB_MIX_1 = [];
    var SUB_MIX_2 = [];
    var SUB_MIX_3 = [];
    var INV_SUB_MIX_0 = [];
    var INV_SUB_MIX_1 = [];
    var INV_SUB_MIX_2 = [];
    var INV_SUB_MIX_3 = [];

    // Compute lookup tables
    (function () {
        // Compute double table
        var d = [];
        for (var i = 0; i < 256; i++) {
            if (i < 128) {
                d[i] = i << 1;
            } else {
                d[i] = (i << 1) ^ 0x11b;
            }
        }

        // Walk GF(2^8)
        var x = 0;
        var xi = 0;
        for (var i = 0; i < 256; i++) {
            // Compute sbox
            var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4);
            sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63;
            SBOX[x] = sx;
            INV_SBOX[sx] = x;

            // Compute multiplication
            var x2 = d[x];
            var x4 = d[x2];
            var x8 = d[x4];

            // Compute sub bytes, mix columns tables
            var t = (d[sx] * 0x101) ^ (sx * 0x1010100);
            SUB_MIX_0[x] = (t << 24) | (t >>> 8);
            SUB_MIX_1[x] = (t << 16) | (t >>> 16);
            SUB_MIX_2[x] = (t << 8)  | (t >>> 24);
            SUB_MIX_3[x] = t;

            // Compute inv sub bytes, inv mix columns tables
            var t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100);
            INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8);
            INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16);
            INV_SUB_MIX_2[sx] = (t << 8)  | (t >>> 24);
            INV_SUB_MIX_3[sx] = t;

            // Compute next counter
            if (!x) {
                x = xi = 1;
            } else {
                x = x2 ^ d[d[d[x8 ^ x2]]];
                xi ^= d[d[xi]];
            }
        }
    }());

    // Precomputed Rcon lookup
    var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];

    /**
     * AES block cipher algorithm.
     */
    var AES = C_algo.AES = BlockCipher.extend({
        _doReset: function () {
            var t;
            
            // Skip reset of nRounds has been set before and key did not change
            if (this._nRounds && this._keyPriorReset === this._key) {
                return;
            }

            // Shortcuts
            var key = this._keyPriorReset = this._key;
            var keyWords = key.words;
            var keySize = key.sigBytes / 4;

            // Compute number of rounds
            var nRounds = this._nRounds = keySize + 6;

            // Compute number of key schedule rows
            var ksRows = (nRounds + 1) * 4;

            // Compute key schedule
            var keySchedule = this._keySchedule = [];
            for (var ksRow = 0; ksRow < ksRows; ksRow++) {
                if (ksRow < keySize) {
                    keySchedule[ksRow] = keyWords[ksRow];
                } else {
                    t = keySchedule[ksRow - 1];

                    if (!(ksRow % keySize)) {
                        // Rot word
                        t = (t << 8) | (t >>> 24);

                        // Sub word
                        t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff];

                        // Mix Rcon
                        t ^= RCON[(ksRow / keySize) | 0] << 24;
                    } else if (keySize > 6 && ksRow % keySize == 4) {
                        // Sub word
                        t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff];
                    }

                    keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t;
                }
            }

            // Compute inv key schedule
            var invKeySchedule = this._invKeySchedule = [];
            for (var invKsRow = 0; invKsRow < ksRows; invKsRow++) {
                var ksRow = ksRows - invKsRow;

                if (invKsRow % 4) {
                    var t = keySchedule[ksRow];
                } else {
                    var t = keySchedule[ksRow - 4];
                }

                if (invKsRow < 4 || ksRow <= 4) {
                    invKeySchedule[invKsRow] = t;
                } else {
                    invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^
                                               INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]];
                }
            }
        },

        encryptBlock: function (M, offset) {
            this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX);
        },

        decryptBlock: function (M, offset) {
            // Swap 2nd and 4th rows
            var t = M[offset + 1];
            M[offset + 1] = M[offset + 3];
            M[offset + 3] = t;

            this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX);

            // Inv swap 2nd and 4th rows
            var t = M[offset + 1];
            M[offset + 1] = M[offset + 3];
            M[offset + 3] = t;
        },

        _doCryptBlock: function (M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) {
            // Shortcut
            var nRounds = this._nRounds;

            // Get input, add round key
            var s0 = M[offset]     ^ keySchedule[0];
            var s1 = M[offset + 1] ^ keySchedule[1];
            var s2 = M[offset + 2] ^ keySchedule[2];
            var s3 = M[offset + 3] ^ keySchedule[3];

            // Key schedule row counter
            var ksRow = 4;

            // Rounds
            for (var round = 1; round < nRounds; round++) {
                // Shift rows, sub bytes, mix columns, add round key
                var t0 = SUB_MIX_0[s0 >>> 24] ^ SUB_MIX_1[(s1 >>> 16) & 0xff] ^ SUB_MIX_2[(s2 >>> 8) & 0xff] ^ SUB_MIX_3[s3 & 0xff] ^ keySchedule[ksRow++];
                var t1 = SUB_MIX_0[s1 >>> 24] ^ SUB_MIX_1[(s2 >>> 16) & 0xff] ^ SUB_MIX_2[(s3 >>> 8) & 0xff] ^ SUB_MIX_3[s0 & 0xff] ^ keySchedule[ksRow++];
                var t2 = SUB_MIX_0[s2 >>> 24] ^ SUB_MIX_1[(s3 >>> 16) & 0xff] ^ SUB_MIX_2[(s0 >>> 8) & 0xff] ^ SUB_MIX_3[s1 & 0xff] ^ keySchedule[ksRow++];
                var t3 = SUB_MIX_0[s3 >>> 24] ^ SUB_MIX_1[(s0 >>> 16) & 0xff] ^ SUB_MIX_2[(s1 >>> 8) & 0xff] ^ SUB_MIX_3[s2 & 0xff] ^ keySchedule[ksRow++];

                // Update state
                s0 = t0;
                s1 = t1;
                s2 = t2;
                s3 = t3;
            }

            // Shift rows, sub bytes, add round key
            var t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++];
            var t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++];
            var t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++];
            var t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++];

            // Set output
            M[offset]     = t0;
            M[offset + 1] = t1;
            M[offset + 2] = t2;
            M[offset + 3] = t3;
        },

        keySize: 256/32
    });

    /**
     * Shortcut functions to the cipher's object interface.
     *
     * @example
     *
     *     var ciphertext = CryptoJS.AES.encrypt(message, key, cfg);
     *     var plaintext  = CryptoJS.AES.decrypt(ciphertext, key, cfg);
     */
    C.AES = BlockCipher._createHelper(AES);
}());

数据源文件名为encryptData.txt,存储对象为json格式对象,初始内容为空对象:{},对象结果为{servergroupid:{servergroupname:"name",detail:{}}}。没用过monogo数据库,好像很接近了:)。

界面很难看,没有美工,姑且不截图了:-)

写在最后——

本来想写个简单的基于JavaScript前端系统,结果发现各个类、对象耦合性非常紧,有违初衷,于是乎又投入了大量的精力和时间进行解耦,改着改着就接近于类MVC结构了。也玩味到了MVC架构的优势。

说实话,如果仅就前端用html做个界面,写一堆js代码实现一下CRUD功能,应该很快。这样反而复杂化了,有什么优势呢?面向对象架构到底死没死,还真不好说,欢迎拍砖!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值