Fastest JavaScript Trim
这是 2007 年的一篇老文:Faster JavaScript Trim.
测试页面:trim-test.html
感兴趣的几个方法:
function trim1(str) {
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
function trim2(str) {
return str.replace(/^\s+/, '').replace(/\s+$/, '');
}
function trim4(str) {
return str.replace(/^\s+|\s+$/g, '');
}
function trim8(str) {
return str.replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
}
时至今日,有些结论已经发生变化:
1. trim1 中使用的正则优化:pre-check of required character and start of string anchor, 在最新的 JS 引擎中已经内置。trim4 也得到了非常好的优化,比 trim1, trim2 都快。
2. trim8 在 IE 中依旧非常有优势。这是因为 IE 对 \S\s (any character token) 有非常好的优化。另外 ?: (non-capturing group) 对性能也有提升。
3. trim10 是非正则实现方案,速度飕飕的。原文中指出关键点:正则在处理过程中无法直接跳转到字符串末尾,从而导致在处理长字符串时耗时稍长。于是有了混合版本 trim11 和 trim11 的改进版本 trim12:
function trim11(str) {
str = str.replace(/^\s+/, '');
for (var i = str.length - 1; i >= 0; i--) {
if (/\S/.test(str.charAt(i))) {
str = str.substring(0, i + 1);
break;
}
}
return str;
}
function trim12(str) {
str = str.replace(/^\s\s*/, "");
var ws = /\s/,
i = str.length;
while (ws.test(str.charAt(--i))) {}
return str.slice(0, i + 1);
}
4. 在原文的回复中,还有利用空间换时间的 map 方案。
5. 对于普通应用,比如 JS 框架,原文作者推荐使用 trim1. 从上面的变化可以看出,目前更好的方案是 trim4. 这也正是当今流行 JS 框架中广泛采用的实现方案。对于特殊应用需要处理长字符串时,推荐采用 trim11.
6. 目前 Firefox 3 和 Chrome 4 中都已原生实现 String.prototype.trim, 对于这些浏览器,原生实现始终是最佳方案。
7. 原文作者的 final note 值得注意:许多开发者习惯用变量缓存正则,这可以避免正则的重复编译,但这对于简单如 trim 的正则来说意义不大。trim 的正则如此简单,编译时间是纳秒级的。而且不少浏览器自身已对最近使用的正则进行缓存,不会存在重复编译的问题。trim13 用来验证此观点,除了 IE, 其它浏览器下,当重复次数 times 变大时,trim13 反而比 trim12 略有优势。
8. 讨论一个有争议的点:对于 JS 类库来说,trim 方法究竟应不应该附加在 String.prototype 上呢?侵入 built-in 对象,会导致for...in产生某些非预期结果。但是,对于 String 来说,用for...in遍历 String 本就是非常不明智的行为。为了这些“愚蠢的”使用方法,而坚决不侵入 built-in 对象,是否真的值得?毕竟从用户角度讲,str.trim() ,比类似$.trim(str)这种写法自然多了。
最后给一个我的方案:
if(!String.prototype.trim) {
var TRIM_REG = /^\s+|\s+$/g;
String.prototype.trim = function() { return this.replace(TRIM_REG, ''); }
}

January 11th, 2010 on 0:42
我之前也做过类似的测试,好像正则中使用条件分支(也就是trim4和你的方案)性能上相对trim2较低的,但是相差也不大。
对于大文本,尾部使用正则也不如循环遍历来得快,一般使用前面正则,后面循环的方案,也就是trim11。
至于trim1这种写法倒是第一次见,不知相较trim2有什么优点,这个正则性能更佳?有空再测试比较一下。
January 11th, 2010 on 0:44
另外关于你的方案中的全局变量,建议写成:
String.TRIM_REG = …
January 11th, 2010 on 7:41
@闲耕:trim1 中,\s\s* 第一个字符 \s 能触发优化:pre-check of required character. 这些优化技术仅对老浏览器有效,新浏览器都已内置这些正则优化。一般情况,trim4 已足够快。
TRIM_REG 不是全局变量,在闭包里。放 String 上感觉不妥,增加了侵入,还不利于压缩。
January 11th, 2010 on 10:23
最后一个争议,我是偏向OOP的人哈哈,方法调用就应该在其对象上~
对象.方法(),而非方法(对象)。
January 11th, 2010 on 13:10
有更新的方法
http://jsgears.com/thread-132-1-1.html
January 11th, 2010 on 15:10
javascript是没有块级作用域的,例如if/else, do/while, try/catch…都块(block),而不是作用域(scope),javascript有效的作用域包括全局和函数作用域。在 Global 的 if 中定义的变量,同样属于 Global 作用域,if 之外同样可以使用这个变量;
至于说到侵入,trim本身已经侵入了。
再至于压缩,全局变量一般不建议压缩,除非有完整的项目语义分析。
January 11th, 2010 on 16:00
@闲耕:我说的闭包不是指 if 的块语句。真实代码是:(function() { /* trim code */ })()
January 15th, 2010 on 11:38
hi,可以请教作者一下,为何在不同的浏览器里面,你那个测试例子页面的customTrim变量有的存在有的不存在吗?
经测试:chrome,opera,safari 为true
ie8,firefox,为undefined
原因何在呢?
January 15th, 2010 on 11:54
还有,貌似konsole.time(name);中的name并没有定义。
应该为undefined,但是为何alert的时候显示为空,并用typeof为String
我并没有在你代码中发现有with语句啊?
January 15th, 2010 on 12:00
@simaopig: customTrim 为 undefined,是因为 ie8 和 firefox 低版本等浏览器不支持原生 trim.
name 是参数值,传了值的。
January 15th, 2010 on 12:19
谢谢。那个undefined的问题我看明白了。真是不好意思啊。呵呵。
不过name的问题我还是不理解,你并没有传参数:
function benchmark(tests, times, wl) {
}
而且你看:http://www.xiaoxiaozi.com/2009/12/17/1674/ 也是name属性可以直接读。难道是window的name属性?
January 15th, 2010 on 12:20
直接在地址栏里面试验也是:javascript:alert(name);
和javascript:alert(window.name);都是会返回一个空javascript:alert(name==false);也是返回的true
January 15th, 2010 on 12:24
打扰了。看来就是这个window.name惹的祸,但是我对这个不太了解。一会儿找几篇文章看一下。
发了这么多个垃圾评论,不好意思哈。
leave a reply