经常看到“面向对象已死”、“面向对象被弃用”、“面向对象已过时”等等论调,作为开发大军里的一枚边际小棋子,实在不敢妄断,也不清楚前沿大佬是怎么看待、分哪几个派别。不过,目前来看,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功能,应该很快。这样反而复杂化了,有什么优势呢?面向对象架构到底死没死,还真不好说,欢迎拍砖!