js_trim()十二种实现

The analysis
Although there are 11 rows in the table above, they are only the most notable (for various reasons) of about 20 versions I wrote and benchmarked against various types of strings. The following analysis is based on testing in Firefox 2.0.0.4, although I have noted where there are major differences in IE6.

1.
-----------------------------------------------------------------
//return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');

function trim(str) {
    return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');

}
-------------------------------------------------------------------
All things considered, this is probably the best all-around approach. Its speed advantage is most notable with long strings — when efficiency matters. The speed is largely due to a number of optimizations internal to JavaScript regex interpreters which the two discrete regexes here trigger. Specifically, the pre-check of required character and start of string anchor optimizations, possibly among others.

2.
-------------------------------------------------------------------
return str.replace(/^\s+/, '').replace(/\s+$/, '');
-------------------------------------------------------------------
Very similar to trim1 (above), but a little slower since it doesn't trigger all of the same optimizations.

3.
-------------------------------------------------------------------
return str.substring(Math.max(str.search(/\S/), 0), str.search(/\S\s*$/) + 1);
-------------------------------------------------------------------
This is often faster than the following methods, but slower than the above two. Its speed comes from its use of simple, character-index lookups.

4.
-------------------------------------------------------------------
return str.replace(/^\s+|\s+$/g, '');
-------------------------------------------------------------------
This commonly thought up approach is easily the most frequently used in JavaScript libraries today. It is generally the fastest implementation of the bunch only when working with short strings which don't include leading or trailing whitespace. This minor advantage is due in part to the initial-character discrimination optimization it triggers. While this is a relatively decent performer, it's slower than the three methods above when working with longer strings, because the top-level alternation prevents a number of optimizations which could otherwise kick in.

5.
-------------------------------------------------------------------
str = str.match(/\S+(?:\s+\S+)*/);
return str ? str[0] : '';
-------------------------------------------------------------------
This is generally the fastest method when working with empty or whitespace-only strings, due to the pre-check of required character optimization it triggers. Note: In IE6, this can be quite slow when working with longer strings.

6.
-------------------------------------------------------------------
return str.replace(/^\s*(\S*(\s+\S+)*)\s*$/, '$1');
-------------------------------------------------------------------
This is a relatively common approach, popularized in part by some leading JavaScripters. It's similar in approach (but inferior) to trim8. There's no good reason to use this in JavaScript, especially since it can be very slow in IE6.

7.
-------------------------------------------------------------------
return str.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, '$1');
-------------------------------------------------------------------
The same as trim6, but a bit faster due to the use of a non-capturing group (which doesn't work in IE 5.0 and lower). Again, this can be slow in IE6.


8.
-------------------------------------------------------------------
return str.replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
-------------------------------------------------------------------
This uses a simple, single-pass, greedy approach. In IE6, this is crazy fast! The performance difference indicates that IE has superior optimization for quantification of "any character" tokens.

9.
-------------------------------------------------------------------
return str.replace(/^\s*([\S\s]*?)\s*$/, '$1');
-------------------------------------------------------------------
This is generally the fastest with very short strings which contain both non-space characters and edge whitespace. This minor advantage is due to the simple, single-pass, lazy approach it uses. Like trim8, this is significantly faster in IE6 than Firefox 2.
Since I've seen the following additional implementation in one library, I'll include it here as a warning:
-------------------------------------------------------------------
return str.replace(/^\s*([\S\s]*)\b\s*$/, '$1');
-------------------------------------------------------------------
Although the above is sometimes the fastest method when working with short strings which contain both non-space characters and edge whitespace, it performs very poorly with long strings which contain numerous word boundaries, and it's terrible (!) with long strings comprised of nothing but whitespace, since that triggers an exponentially increasing amount of backtracking. Do not use.

A different endgame
There are two methods in the table at the top of this post which haven't been covered yet. For those, I've used a non-regex and hybrid approach.

After comparing and analyzing all of the above, I wondered how an implementation which used no regular expressions would perform. Here's what I tried:

-------------------------------------------------------------------
function trim10 (str) {
 var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';
 for (var i = 0; i < str.length; i++) {
  if (whitespace.indexOf(str.charAt(i)) === -1) {
   str = str.substring(i);
   break;
  }
 }
 for (i = str.length - 1; i >= 0; i--) {
  if (whitespace.indexOf(str.charAt(i)) === -1) {
   str = str.substring(0, i + 1);
   break;
  }
 }
 return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';
}
-------------------------------------------------------------------
How does that perform? Well, with long strings which do not contain excessive leading or trailing whitespace, it blows away the competition (except against trim1/2/8 in IE, which are already insanely fast there).

Does that mean regular expressions are slow in Firefox? No, not at all. The issue here is that although regexes are very well suited for trimming leading whitespace, apart from the .NET library (which offers a somewhat-mysterious "backwards matching" mode), they don't really provide a method to jump to the end of a string without even considering previous characters. However, the non-regex-reliant trim10 function does just that, with the second loop working backwards from the end of the string until it finds a non-whitespace character.

Knowing that, what if we created a hybrid implementation which combined a regex's universal efficiency at trimming leading whitespace with the alternative method's speed at removing trailing characters?

-------------------------------------------------------------------
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;
}
-------------------------------------------------------------------
Although the above is a bit slower than trim10 with some strings, it uses significantly less code and is still lightning fast. Plus, with strings which contain a lot of leading whitespace (which includes strings comprised of nothing but whitespace), it's much faster than trim10.

In conclusion…


Since the differences between the implementations cross-browser and when used with different data are both complex and nuanced (none of them are faster than all the others with any data you can throw at it), here are my general recommendations for a trim method:

 

Use trim1 if you want a general-purpose implementation which is fast cross-browser.


Use trim11 if you want to handle long strings exceptionally fast in all browsers.


To test all of the above implementations for yourself, try my very rudimentary benchmarking page. Background processing can cause the results to be severely skewed, so run the test a number of times (regardless of how many iterations you specify) and only consider the fastest results (since averaging the cost of background interference is not very enlightening).

 

As a final note, although some people like to cache regular expressions (e.g. using global variables) so they can be used repeatedly without recompilation, IMO this does not make much sense for a trim method. All of the above regexes are so simple that they typically take no more than a nanosecond to compile. Additionally, some browsers automatically cache the most recently used regexes, so a typical loop which uses trim and doesn't contain a bunch of other regexes might not encounter recompilation anyway.


--------------------------------------------------------------------------------

Edit (2008-02-04): Shortly after posting this I realized trim10/11 could be better written. Several people have also posted improved versions in the comments. Here's what I use now, which takes the trim11-style hybrid approach:
-------------------------------------------------------------------
function trim12 (str) {
 var str = str.replace(/^\s\s*/, ''),
  ws = /\s/,
  i = str.length;
 while (ws.test(str.charAt(--i)));
 return str.slice(0, i + 1);
}
-------------------------------------------------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值