JavaScript字符串String和Array操作的有趣方法


Posted in Javascript onDecember 18, 2012

字符串和数组在程序编写过程中是十分常用的类型,因此程序语言都会将String和Array作为基本类型,并提供许多字符串和数组的方法来简化对字符串的操作。JavaScript里面也提供了String类型和Array类型,并且有很多基本的String方法和Array方法来方便地对字符串进行合并、查找、替换、截取等处理。

JavaScript作为一个脚本语言,又提供了一种动态解析运行的机制,而这特性,又让使得在String操作的时候出现一些结合使用Array的有趣方法。这些方法可能有些偏门有点奇怪,但有时在效率、可读性、复用性上表现得却更好。

重复字符串
常常我们想要把字符串多次打印出来(比如想要个分割线),我们就需要将一个字符串重复多次, 可惜JavaScript并没有提供类似repeat这样的方法。当然我们可以用循环来拼接出来,但是我们可以利用JavaScript中Array的join方法来实现repeat

function repeat(str, n) { 
var arr = new Array(n+1); 
return arr.join(str); 
} 
//output: 
//-------

利用n+1个Array元素产生的n个间隙,再以目标字符串来拼接,我们就能得到字符串重复的功能。
扩展String的prototype使方法应用于所有字符串
JavaScript的对象继承和方法寻找是基于原型链(prototype chain),所有使用着的字符串都可以说是继承于String的对象,我们可以为String对象的prototype添加方法或者属性,这样该方法就可以应用到所有我们使用的对象上了。比如上边的repeat方法,就可以改成:
String.prototype.repeat = function(n) { 
var arr = new Array(n+1); 
return arr.join(this); 
}; 
document.write('-'.repeat(21)); 
//output: 
//---------------------

然后,直接通过字符串调用repeat方法,就可以得到跟上边一样的结果。
这可以让我们实现对字符串方法的扩充,简洁对字符串的操作,但是这会“污染”了JavaScript的String,当代码被转到其他文件但是那个文件下并没有得到这段扩充,就可能会造成找不到该方法;另外,调用prototype扩展方法比直接调用方法要稍微“慢”一些,因为JavaScript会先去在字符串对象自身的方法中尝试寻找,再找到String的prototype的方法;再者也许在将来我们扩充的方法(比如repeat)变成了标准方法了,再使用这代码就会覆盖了标准方法,得到不一致的结果。

但是忽略这些考虑,扩充JavaScript标准类型的prototype还是会给编程带来许多的遍历。

用Array作StringBuilder
在很多高级语言中,加号(+)在字符串的操作中被赋予了更多的意义:作为字符串拼接的操作符。不过在Java和C#中,我们也知道如何频繁进行字符串拼接的操作,使用加号(+)就会产生效率问题,因此在这种情况下就会推荐使用StringBuilder。

JavaScript也支持使用加号(+)来进行字符串拼接,那么也会有存在效率问题呢。可是JavaScript并没有提供StringBuilder这样的类。

其实在Java,C#中使用StringBuilder时,我们多数也是用append方法,而很少会用insert。好在JavaScript的Array是不限大小自动增长的,所以我们就可以利用Array来做StringBuilder,最后再join空字符串来拼接出目标字符串。

var sb = []; 
for(var i = 0; i <=21; i++) { 
sb.push(i); 
} 
document.write(sb.join('')); 
//output: 
//0123456789101112131415161718192021

到底是用Array做StringBuilder还是直接字符串拼接,jsPerf上有过很多testcases比较两者的效率,但是因为初始值、环境、字符串长度等原因,所以结果不一。其实字符串内容不是很大,或者可以使用多个加号(+)组合在一起,那么字符串拼接还是可以的;若是在代码不同地方对同一字符串变量进行追加,那么可能使用Array配合join会更好。

用split替代字符串的子串查找和替换
在字符串的操作中,很常出现的就是想要从字符串中查找一个子字符串是否存在,然后截取出该字符串,抑或是将该子字符串替换成其它字符串。

比如给一个文件名,希望根据点(.)分割获取基本名和后缀名。先来看看使用标准String方法实现的这些操作:

function getBaseName(str) { 
var pos = str.lastIndexOf('.'); 
if(pos < 0)return str; 
return str.substring(0, pos); 
} 
function getExtension(str) { 
var pos = str.lastIndexOf('.'); 
if(pos < 0)return ''; 
return str.substr(pos+1); 
} 
var fileName = 'hello_world.js'; 
document.write(getBaseName(fileName)); 
document.write('<br />'); 
document.write(getExtension(fileName)); 
//output: 
//hello_world 
//js

(除了substr和substring外,JavaScript还有slice都可以用来获取字符串的子串,但也正是因为选择太多,常常让我在出现选择恐慌,还有位置是该不该+1,对负数是如何处理也让我揪心。)

之前看到可以通过join把数组变成字符串,也可以利用String的split的方法把字符串变成数组。对于上边取文件名及扩展名的问题,我们就可以根据“.”把文件名分裂成数组各个部分,那么假如得到的数字大于1(后缀名存在),则所得数字的最后一个元素就是文件的扩展名了:

function getBaseName(str) { 
var segs = str.split('.'); 
if(segs.length > 1) segs.pop(); 
return segs.join('.'); 
} 
function getExtension(str) { 
var segs = str.split('.'); 
if(segs.length <= 1)return ''; 
return segs.pop(); 
}

考虑到文件名中可能包含多个“.”,所以我们还是需要用“.”把除了最后一部分外的各个部分join回来。
看到可以对字符串先split再join,就可以想到,我们可以想到对于这两个方法的参数可以传入不同的字符串,这样就起到了代替String的replace方法进行子串替换的功能了,而且还是全局替换。
比如希望把所有的下划线(_)替换成横杠(-):
var str = 'hello_from_ider_to_world'.split('_').join('-'); 
document.write(str); 
//Output: 
// hello-from-ider-to-world

相对于String的replace方法,该方法的有点在于:可以实现全局替换;而若要让replace能够全局替换,则需要传入正则表达式对象(RegExp)而不能是字符串作为第一参数。

replace可接受RegExp、Function作为参数
很多人知道String的replace方法是用来替换字符串子串的,也可能知道它可以接受正则表达式作为第一参数,而且如何要替换所有出现的地方,就必须要用RegExp并包含global标记。
比如之前的替换操作,用replace就应该是:

var str = 'hello_from_ider_to_world'.replace(/_/g, '-'); 
document.write(str);

再比如很常用的trim方法,虽然JavaScript并没有提供我们也可以自己很快的实现:
String.prototype.trim = function() { 
return this.replace(/^\s+|\s+$/g, ''); 
};

我们知道正则表达式一个很强大的功能就是向后引用(Back Reference),实际上JavaScript的replace不仅在第一个参数内做向后引用,而且在替换字符串上,也可以进行向后引用,只是很多地方可能用反斜杠(\)加数字作为标示而JavaScript则是用美刀($)加数字作为标示。
var friends = 'friends of Ider, friend of Angie'; 
var result = friends.replace(/(friends?) of (\w+)/g, "$2's $1"); 
document.write(result); 
//output: 
//Ider's friends, Angie's friend

通过在替换字符串里面进行向后引用,我们很快就把“朋友 of 谁谁谁”变成了“谁谁谁的朋友”。如果还要更复杂点怎么办呢?没有关系,replace还能接受Function作为参数作为回调函数,其中函数的第一个参数是整个匹配中的字符串,之后每一个代表个个向后引用匹配的,函数的返回值则是作为替换的字符串。所以很多使用,函数参数都会用$0, $1, $2来表示。来看个例子:
var friends ="friends of mine, friend of her and friends of his"; 
var result = friends.replace(/(friends?) of (\w+)/g, 
function($0, $1, $2) { 
if($2 == 'mine') $2 = 'my'; 
return $2 + ' ' + $1; 
}); 
document.write(result); 
//output: 
//my friends, her friend and his friends

通过回调函数就可以实现很多很负责的字符串匹配了。至于效率,就先不考虑了。
Javascript 相关文章推荐
JavaScript Event学习第九章 鼠标事件
Feb 08 Javascript
jquery append 动态添加的元素事件on 不起作用的解决方案
Jul 30 Javascript
jQuery四种选择器使用及示例
Jun 05 Javascript
微信小程序 slider的简单实例
Apr 19 Javascript
vue中路由验证和相应拦截的使用详解
Dec 13 Javascript
微信小程序实现折叠面板
Jan 31 Javascript
JavaScript代码调试方法实例小结
Jan 05 Javascript
详解使用JWT实现单点登录(完全跨域方案)
Aug 02 Javascript
详解基于Vue/React项目的移动端适配方案
Aug 23 Javascript
js实现点击图片在屏幕中间弹出放大效果
Sep 11 Javascript
node中短信api实现验证码登录的示例代码
Jan 20 Javascript
jquery插件实现代码雨特效
Apr 24 jQuery
学习js在线html(富文本,所见即所得)编辑器
Dec 18 #Javascript
javascript jscroll模拟html元素滚动条
Dec 18 #Javascript
Android中资源文件(非代码部分)的使用概览
Dec 18 #Javascript
js获取单选框或复选框值及操作
Dec 18 #Javascript
JQuery触发radio或checkbox的change事件
Dec 18 #Javascript
Jquery为单选框checkbox绑定单击click事件
Dec 18 #Javascript
jQuery实现form表单reset按钮重置清空表单功能
Dec 18 #Javascript
You might like
Discuz 6.0+ 批量注册用户名
2009/09/13 PHP
PHP数组无限分级数据的层级化处理代码
2012/12/29 PHP
WordPress中邮件的一些修改和自定义技巧
2015/12/15 PHP
js String对象中常用方法小结(字符串操作)
2012/01/27 Javascript
浅析XMLHttpRequest的缓存问题
2013/12/13 Javascript
javascript在子页面中函数无法调试问题解决方法
2014/01/17 Javascript
jquery获取当前点击对象的value方法
2014/02/28 Javascript
javascript 获取浏览器版本
2015/01/21 Javascript
JavaScript针对网页节点的增删改查用法实例
2015/02/02 Javascript
jQuery制作效果超棒的手风琴折叠菜单
2015/04/03 Javascript
JavaScript中var关键字的使用详解
2015/08/14 Javascript
老生常谈遮罩层 滚动条的问题
2016/04/29 Javascript
微信小程序 出现错误:{&quot;baseresponse&quot;:{&quot;errcode&quot;:-80002,&quot;errmsg&quot;:&quot;&quot;}}解决办法
2017/02/23 Javascript
JavaWeb表单及时验证功能在输入后立即验证(含用户类型,性别,爱好...的验证)
2017/06/09 Javascript
React进阶学习之组件的解耦之道
2017/08/07 Javascript
Echart折线图手柄触发事件示例详解
2018/12/16 Javascript
vue实现路由切换改变title功能
2019/05/28 Javascript
微信小程序自定义navigationBar顶部导航栏适配所有机型(附完整案例)
2020/04/26 Javascript
react 生命周期实例分析
2020/05/18 Javascript
Vue 请求传公共参数的操作
2020/07/31 Javascript
vue组件中节流函数的失效的原因和解决方法
2020/12/02 Vue.js
学习python中matplotlib绘图设置坐标轴刻度、文本
2018/02/07 Python
Python实现压缩文件夹与解压缩zip文件的方法
2018/09/01 Python
Django中如何防范CSRF跨站点请求伪造攻击的实现
2019/04/28 Python
numpy.ndarray 实现对特定行或列取值
2019/12/05 Python
python数据处理——对pandas进行数据变频或插值实例
2020/04/22 Python
Python并发concurrent.futures和asyncio实例
2020/05/04 Python
python中turtle库的简单使用教程
2020/11/11 Python
Django web自定义通用权限控制实现方法
2020/11/24 Python
HTML5自定义属性前缀data-及dataset的使用方法(html5 新特性)
2017/08/24 HTML / CSS
Banana Republic欧盟:美国都市简约风格的代表品牌
2018/05/09 全球购物
信息工程学院毕业生推荐信
2013/11/05 职场文书
中医专业应届生求职信
2013/11/17 职场文书
教师绩效考核方案
2014/01/21 职场文书
好学生评语大全
2014/05/05 职场文书
mysql 获取时间方式
2022/03/20 MySQL