Javascript拼接HTML字符串的方法列举及思路
转载过来,去掉一些废话吧。
目标:
方便的拼接字符串,不使用让人眼晕的+=。使用过程如下:
1,先创建一个作为“模板”的字符串,如:’My name is ${name},I\’m ${age}.’
2,传一个对象进去,其中包含了你要填进模板的值,如:{name:’LIX’,age:11}
3,最后你就能得到你想要的字符串了,如:My name is LIX,I’m 11.
调用方法:
mix('My name is "${name}",I\'m "${age}".',{name:'LIX',age:11})
或
mix('My name is "${0}",I\'m "${1}".',['LIX',11])
几种实现:
1,replace+regexp
在司徒正美的文章里看到的。下面是简化后的代码:
function mix (str,group) {
str = str.replace(/\$\{([^{}]+)\}/gm,function (m,n) {
return (group[n] != undefined) ? group[n] : '';
})
return str;
}
其中function里面的m,n值得讲一下。他们是从哪儿传的值呢?就是正则表达式。string的replace方法,如果第2个参数是个函数的话,那函数的第1个参数值肯定就是“当前匹配到的字符串”。
但这里的函数有了两个参数,第2个参数n,是什么?他就是你正则中的分组的第1组(被第1组()包起来的部分)——也就是说,如果你愿意,还可以有很多组,然后replace的函数就可以有很多个参数了。
replace接受一个处理函数,其第一个参数是当前匹配到的子字符串,后面的参数就依次是正则匹配到的的第1组,第2组…
而函数中的return则是重中之重,如果没有返回,那么替换就不会发生。replace正是用return回来的子串替换掉之前匹配到的子串的(就是参数m).
这个方法原理简单易懂,代码也少,但有个问题,我测试的时候发现这个比使用普通的+=串联字符串慢了10倍不止!!太让人心寒了啊
而我对这种方便又好用的拼字符串的方法非常眼热,所以我只能考虑如何去提高其效率了。
方法二
既然replace+正则表达式效率不高,我就打算试试不用replace的方法。而查找字段标签(即${name}这样的)还是用正则来做,找到之后,我们把字符串在此标签之前的部分,以及之后的部分都截取出来——恰好去掉${name}这一截,然后用+直接连上此标签对应的值(例子里是LIX),如此循环。
代码如下:
function loopMix0 (str,group) {
var reg = /\$\{([^{}]+)\}/gm, res;
while(res = reg.exec(str)) {
str = str.substr(0,res.index)+((group[res[1]] != undefined) ? group[res[1]] : '')+str.substr(res.index+res[0].length);
}
return str;
}
正则的exec方法是个比较奇特的方法,因为他不会一次把所有符合匹配条件的子串都返回,而是每次只返回当前匹配到的1个子串,详细格式如此:
[当前匹配到的子串,(如果正则有分组,那么这里就是依次按分组匹配到的值,组1,组2...),index(这是当前匹配到的子串的index)]
如果要靠exec把所有能匹配的都给匹配了,那只有循环了。exec每次匹配后,都会改变他自己的lastIndex属性,以便下次exec的时候不会又把以前匹配过的再匹配一次。当exec没有返回结果的时候,就表示全部匹配完成了。
这样就没有用replace,而是用了字符串的原生方法,效率应该有提高吧?
现实是残酷的,此方法和方法1的效率几乎没提高。这个方法的缺点很明显,就是和replace一样,每次循环中还是对整个字符串做操作(不停的赋予新值,然后用新值代入下次循环),效率当然不能提高。
方法三
明白了方法2的缺点,要做改进就很简单了。我先新建一个空字符串,然后还是按上面的循环,只是每次都依次把字段标签前的部分,字段标签对应值,字段标签后头的部分,连接到这个空字符串上。这样,虽然这个空字符串越来越长了,但我们再也没有每次都对原始字符串进行修改了——原始字符串才是最长的好吧!!
代码如下:
function loopMix1 (str,group) {
var reg = /\$\{([^{}]+)\}/gm, res,returnString = '',start = 0;
while(res = reg.exec(str)) {
returnString += str.substring(start,res.index)+((group[res[1]] != undefined) ? group[res[1]] : '');
start = res.index+res[0].length;
}
returnString += str.substr(start);
return returnString;
}
其中有个变量start,保存着下一次str开始截取的起始位置,很重要。
PS:循环结束后还要在returnString上加上原始字符串的最后一截哟,不然你就得不到你“预期中的那么长”了。
这代码有个变化就是不再是用的substr了,而是用的substring。因为substr的第2个参数是length,不再适合这里。
此方法比方法2快1倍有余!
说起substr和substring,就不得不提一个“万人迷”(迷惑不清的迷):substr和substring的第2个参数各是什么意思?如何才能不混淆?
其实很简单:substr比substring短得多,所以它迫切地需要“长度”,所以他的第2个参数是length.
方法四
方法3已经不错了,但我是个精益求精的人。方法3在理论上还有个缺点,就是原始字符串str始终没有改变,每次循环的时候都一样长,会不会拖累正则以及substring的效率呢?
所以我就每次循环都把str变短了,反正前半截本来也是再也不要了的嘛。代码如下:
function loopMix2 (str,group) {
var reg = /\$\{([^{}]+)\}/gm, res,returnString = '',start = 0;
while(res = reg.exec(str)) {
returnString += str.substring(0,res.index)+((group[res[1]] != undefined) ? group[res[1]] : '');
start = res.index+res[0].length;
str = str.substr(start);
reg.lastIndex = 0;
}
returnString += str;
return returnString;
}
代码中不只是把str变短了,还重置了reg的查询下标,以防万一。
这样是不是比上个方法更进一步?答案是否定的,此方法比方法3慢,原因还是因为在循环里操作过多,导致效率不增反降。不过比方法1,2要快就是了。
方法五
由于我们的字段标签${name}是比较容易识别的,在不故意把str弄错的情况下,我们可以用string的原生方法:indexOf来将字段标签提取出来,然后拼接。
思路是先找到’${‘,再按照得到的index,找到紧邻的’}',然后取中间的值,也就得到了字段标签的key值,然后从group中得到对应值,拼进结果字符串中。代码如下:
function loopMix3 (str,group) {
var index=0,close=0,returnString = '',name = '';
while((index = str.indexOf('${',index)) !== -1) {
returnString += str.substring(close,index);
close = str.indexOf('}',index);
name = str.substring(index+2,close);
returnString += (group[name] != undefined) ? group[name] : ''
index = close;
close +=1;
}
returnString += str.substr(close);
return returnString;
}
要点:其中要特别注意的是要随时改变indexOf查找的起始位置(index),以及substring开始截取的位置(close)。
这个方法完全没用正则,但效率还是没有提高,完全比不上方法3,难道也是循环中操作太多?
PS:此方法的代码有bug,比如字符串如下:’My name is “${name}”,this is a half ${name .{$name}’,这也就是我说的“故意”把字符串弄错的情况,不过这个bug也是很好修复的,只要在找到一个${后,在查找}之前,再次继续查找${,如果有结果,则continue下次循环。不过如此一来,又多了一个判断,效率就更差了。
方法六
经常是写着这段代码,忽然就想起了另一种思路。比如此方法。
string有个自带方法split,可以把字符串按某个分隔符拆分成数组,而且split支持正则表达式!也就是说我可以把我的原始字符串按${name}这样的字段标签折成数组!
然后呢,虽然把字符串折开了,但我们并没有得到所有的字段标签啊?string有个match方法,他能返回所有匹配参数的子串,而且他也接受正则,返回的也是个数组!
所以我现在拿这个正则做了两个操作,一是将其作为分隔符把原字符串拆了,二是用它将原字符串里所有的字段标签提取出来。
现在我们有了两个数组,如果把这两个数组从头至尾拼合起来,恰好可以得到原始字符串!当然,我们肯定不能按原样拼。。。
现在我们要循环数组并拼接了。在这之前先问大家两个问题:
1.同一个字符串split与match同一个正则操作后,返回的数组哪个长?
2.'${name}${name}${name}${name}${name}${name}${name}${name}'.split('${name}')返回的数组是哪样的?
问这两个问题是很重要的,与此功能函数的实现密不可分。
很容易就能发现,match返回的数组永远比split返回的数组length少1!所以呢,抱着循环尽量要短的宗旨,我们要对match返回的数组做循环而不是对split.
代码如下:
function matchMix(str, group) {
var reg = /\$\{[^{}]+\}/gm;
var strArr = str.split(reg);
var labelArr = str.match(reg);
var returnString = '',
i = 0,
label, len = labelArr.length;
for (; i < len; i++) {
label = labelArr[i].slice(2, -1);
returnString += strArr[i] + (group[label] != null ? group[label] : '');
}
return returnString + strArr[i];
}
PS:注意循环结束后还要为结果字符串加上split数组的最后一项啊!切记!
此方法比方法3要稍快一点,不过差距很小。我猜想在字符串比较长的情况下应该是此方法占优。
思路之外的优化
拿原始方法mix(replace+regexp)来说,他的效率还有没有办法提高呢?答案是有!
上面所有的思路,大家可以看到我用的是同一个regexp,即/\$\{([^{}]+)\}/gm,他是分了组的。而我们这里需要的匹配是很简单的,其实可以不分组!因为我们只需要得到${name},就能很方便的得到name:用slice截断一下就行了!
所以更改后的mix如下:
//原
function mix (str,group) {
return str.replace(/\$\{([^{}]+)\}/gm,function (m,n) {
return (group[n] != void 1) ? group[n] : '';
})
}
//slice版
function mix1 (str,group) {
return str.replace(/\$\{[^{}]+\}/gm,function (m,n) {
n = m.slice(2,-1);
return (group[n] != void 1) ? group[n] : '';
})
}
单纯用此两者对比,效率孰高孰低?经测试,在所有浏览器下,mix1的效率都有略微提高
由此看来,正则表达式的效率实在是有待改进。
下面是一些相关测试:
此改进方法同样适合于其他思路。
总结
除了现成的方法1,后面的方法可以说都是我现想出来的,但是结果差强人意,并没有如我所愿效率越来越优的情况,只能说锻炼了一下思路吧。
如果这个结果不算打击,那我再告诉大家一个“振奋人心”的消息吧:IE9下,效率最高的是方法1,即原始replace+regexp的方法!后续所有方法都算白瞎了,哈哈!
不过IE9下最快的replace方法,也没有chrome下最慢的replace方法执行的次数多。
说到这里,我要说一下:我是用jsperf.com测试的。测试地址
jsperf不但能对比,且每个测试都有执行次数,IE9下replace虽然效率最高,但执行次数还是赶不上chrome下replace的执行次数。
测试地址里面已经有6个版本,原因嘛是因为我测试着突然又想出了新思路,而jsperf里加新测试代码就要新开版本。其中版本6是方法最全的。
经过反复在各浏览器里做测试,我发现:
1,chrome的效率是最快的,但测试结果非!常!不!稳!定!经常这次运行和下次运行完全是两个结果
2,firefox的效率比chrome差些,但稳定,测试结果也与chrome结论一致
3,IE9效率最差!结论也很奇葩!
其他测试
原文:http://jo2.org/javascript-join-html-string/
Javascript拼接HTML字符串的方法列举及思路的更多相关文章
- JavaScript拼接html字符串时截断问题
在项目中碰到一个问题,就是JavaScript拼接html标签时,里面特殊字符会有些问题,比如单引号截断配对,导致后面的内容不显示或显示错误.在此记录一下. 下面贴一段简化的代码,若有描述不清的地方还 ...
- mysql 将行拼接成字符串的方法
见代码: ;//保证可以拼接足够长的字符串,没它 数据量大时会截断结果1 group by videoType 效果如下:
- 通过拼接SQL字符串实现多条件查询
一.通过拼接SQL字符串的方法的好处是: 1.方便查询条件的扩展. 2.简化业务逻辑的判断. 二.例子: 1.界面设计 2.点击查询的代码 /// <summary> /// 按条件查询 ...
- C 长字符串换行方法
C中字符串有时候会出现很长的情况,如果不换行书写查看起来很不方便. 长字符串拆分成多行处理也是C规范的一部分. 方法1. 利用双引号" " ,将长字符串分成多个子串换行,C会自动无 ...
- 【批量加入】-拼接sql字符串
如今做的一个项目须要用到批量加入,可是封装的底层没有这种方法,所以自食其力,自己来写.我们用的是拼接sql字符串的方法来实现功能. 详细实现流程:首先将须要的数据存储到实体的list中,然后将这个li ...
- 【批量添加】-拼接sql字符串 标签: 批量添加 2015-12-13 17:49 2070人阅读 评论(33)
现在做的一个项目需要用到批量添加,但是封装的底层没有这个方法,所以自食其力,自己来写.我们用的是拼接sql字符串的方法来实现功能. 具体实现流程:首先将需要的数据存储到实体的list中,然后将这个li ...
- js--你需要知道的字符串使用方法(含es6及之后)
前言 字符串作为 JavScript 的基本数据类型,在开发以及面试过程中作为程序员对基础掌握情况的重要考点,本文来总结一下字符串的相关属性以及用法.包含了ES6中的一些新语法特性. 正文 1.字符串 ...
- JavaScript中字符串的方法:charAt()、charCodeAt()、indexOf()、lastIndexOf()、substr()、slice()、substring()、search()、replace()、split()、concat()、toLowerCase()、toUpperCase()
1.字符创的创建: //1.通过new 来创建 var str = String("javascript"); //2.3.直接使用字面量进行创建 var str='html5'; ...
- Js 字符串拼接的两种方法
字符串拼接的两种方法 用数组的方法的好处是:避免变量重新定义.赋值 <!DOCTYPE html> <html lang="en"> <head> ...
随机推荐
- AtCoder Regular Contest 083 E - Bichrome Tree
题目传送门:https://arc083.contest.atcoder.jp/tasks/arc083_c 题目大意: 给定一棵树,你可以给这些点任意黑白染色,并且赋上权值,现给定一个序列\(X_i ...
- 洛谷p1115 最大子段和
题目链接: 最大子段和 题目分析: 动态规划O(n)求解,设f[i]表示以i为终点的最大子段和 分两种情况: 若f[i-1]>0,则显然f[i]=f[i-1]+a[i](a[i]必须包含在内) ...
- laravel oauth2.0 文件上传报错
报错信息: "message": "Invalid stream or file provided for UploadedFile", " ...
- IOS状态栏
IOS状态栏是什么地方? 它是IOS设备屏幕顶部显示信号以及电池的区域.状态栏默认的高度是20像素,状态栏在软件开发中有何作用?联网应用中可在自动帮用户下载数据时使用,推荐在状态栏中予以显示.状态栏可 ...
- 如何实现Windows宿主系统和虚拟机ubuntu系统文件互相访问
我的宿主操作系统是Windows 10,使用Oracle的Virtual Box安装了Ubuntu. 因为工作需要我经常得在两个系统之间互相拷贝一些数据,下面是具体步骤,可以实现Windows 10和 ...
- 一个SAP开发人员的双截棍之路
由于种种原因,Jerry最近加入了SAP成都研究院的一个演讲俱乐部,这个俱乐部主要是提高大家的英语演讲能力. 说来Jerry也是大一下期和大二上期一次性高分通过四六级考试的,但是当毕业进入SAP成都研 ...
- macos openssl 生成rsa证书 -mark
创建私钥 openssl genrsa -out rsa_private_key.pem 1024 创建无密码私钥 openssl pkcs8 -topk8 -inform PEM –nocrypt ...
- JavaScript-基础类型和运算符
JavaScript-基础类型和运算符 P02.稍微了解 1.js代码需要编写到script标签中 <script type="text/javascript"> 此处 ...
- MySQL-01 MySQL数据库安装指南
学习要点 MySQL数据库的安装和设置 下载mysql mysql官网:https://www.mysql.com/downloads/ 主要版本: Oracle MySQL Cloud Servic ...
- 01Ping程序的设计
1.Ping程序设计具体设计任务 1.1 实验目的 PING程序是我们使用的比较多的用于测试网络连通性的程序.PING程序基于ICMP,使用ICMP的回送请求和回送应答来工作.由计算机网络课程知道,I ...