当前位置: 代码迷 >> JavaScript >> String.prototype.replace 的 javascript 兑现比较
  详细解决方案

String.prototype.replace 的 javascript 兑现比较

热度:621   发布时间:2012-11-22 00:16:41.0
String.prototype.replace 的 javascript 实现比较

?

chrome :

??

以前初学java时,总是被建议多看 jdk源码可以大大增强功力,后来想把这套也搬到javascript的学习过程中,无奈发现本来应该算作javascript 基础api范畴的 String , Array ... 等都是 native code ,但现在不一样了,chrome V8 来了,我不懂他是怎么实现的 ,但是 javascript 中的基础 api 都是用 javascript 自身实现的(alert(String.prototype.replace);),和 java 越来越接近了,大概由于 v8 的作者 以前就是搞jvm的,觉得这些东西还是像 java 一样作为工具包提供比较好吧。

?

V8 源码@google code

?

V8 作者的解释:

?

All library functions are implemented in JavaScript
Examples: Array.prototype.join, String.prototype.replace


The benefits of not implementing it inside the engine:
1.Keeps the core engine cleaner
2.Easier to change and extend
3.Capitalizes on the performance of the JavaScript compiler


One drawback is startup time ~30 ms ...(每次启动编译库代码)


启动慢的解决之道:

V8 allows saving the initial heap in a snapshot
1.Serialized form is very fast to read
2.Includes pre-compiled code for JavaScript builtins
3.Integrated in the build system and embedded directly in V8


With snapshots, the startup time is reduced to 4-8 ms


???? String.prorotype.replace 应该还算复杂,特别是 replacement可以为函数这个特性是java等静态没有的。


????? 看一下String的replace的主要框架实现吧(最复杂的正则表达式 + 函数替换 ),加了点注释便于理解。详见:String.js

?

?

/*
	regexp 正则表达式
	replace 为函数 function(match,capture1,capture2 ...){
	}
*/
function StringReplaceRegExpWithFunction(subject, regexp, replace) {
  //先做一次正则match,因为下面循环中需要前一次的匹配结束位置,便于写循环
  var lastMatchInfo = DoRegExpExec(regexp, subject, 0);
  if (IS_NULL(lastMatchInfo)) return subject;
	
	/*
		类似于Stringbuilder ,便于从subject截取指定范围子串,存在自己的数组
		generate 时用常见的 join 即可。
	*/
  var result = new ReplaceResultBuilder(subject);
  /*
  	如果正则表达式带g,要全部替换
  */
  if (regexp.global) {
    //初始上一次匹配成功位置为 0
    var previous = 0;
    do {
      
      //上一次匹配位置到这次匹配成功开始位置要放在结果里
      result.addSpecialSlice(previous, lastMatchInfo[CAPTURE0]);
      
      //当前匹配成功开始位置,match.index
      var startOfMatch = lastMatchInfo[CAPTURE0];
      
      //当前匹配成功结束位置,相当于 regexp.lastIndex
      previous = lastMatchInfo[CAPTURE1];
      
      
      //当前匹配传给replacement函数得到替换结果加入结果集合
      result.add(ApplyReplacementFunction(replace, lastMatchInfo, subject));
      
      
      //如果只匹配一个字。。。相当于 regexp == /[\s\S]/
      if (previous == startOfMatch) {
        // 确实没越界,加入结果集合
        if (previous < subject.length) {
          result.addSpecialSlice(previous, previous + 1);
        }
        //下次就要从previous开始加入集合了。
        previous++;
      }

      //没越界,继续分析
      lastMatchInfo = (previous > subject.length)
          ? null
          : DoRegExpExec(regexp, subject, previous);
          
    //没有match就结束      
    } while (!IS_NULL(lastMatchInfo));


		//最后一次匹配结束位置到字符串结尾
    if (previous < subject.length) {
      result.addSpecialSlice(previous, subject.length);
    }
  } 
  
  //不是 g 正则,简单
  else { 
    result.addSpecialSlice(0, lastMatchInfo[CAPTURE0]);
    var endOfMatch = lastMatchInfo[CAPTURE1];
    result.add(ApplyReplacementFunction(replace, lastMatchInfo, subject));
    
    result.addSpecialSlice(endOfMatch, subject.length);
  }
	
	
	//结果数组 join 了,比字符串简单加起来快呢。
  return result.generate();
}

?

?

dean.edwards

?

其实早在 ie7刚发布时,大牛 dean.edwards 写了个提升ie7以下浏览器到ie7功能的 ie7.js ,其中就用javascript实现了 正则+函数替换的功能,据说ie5没有这个功能?。。。
看下 吧,很精巧的地方是 dean.edwards 直接不用正则表达式的 g exec() 功能,而是动态修改当前要匹配的字符串,减少的状态记忆(chrome 的 preivous)。

?

?

// Fix String.replace (Safari1.x/IE5.0).
//regexp.global 也不可靠?直接判断正则有没有g吧
  var GLOBAL = /(g|gi)$/;
  var _String_replace = String.prototype.replace; 
  String.prototype.replace = function(expression, replacement) {
    if (typeof replacement == "function") { // Safari doesn't like functions
      if (expression && expression.constructor == RegExp) {
        var regexp = expression;
        var global = regexp.global;
        //regexp.global 也不可靠?直接判断正则有没有g吧
        if (global == null) global = GLOBAL.test(regexp);
        // we have to convert global RexpExps for exec() to work consistently
        //很精巧,即使是g 全局的也要剥离掉 ,使得全局不全局可以很统一和谐的对待
        if (global) regexp = new RegExp(regexp.source); // non-global
      } else {
        regexp = new RegExp(rescape(expression));
      }
      var match, string = this, result = "";
      while (string && (match = regexp.exec(string))) {
      	//匹配项替换后结果以及匹配前缀放入结果
        result += string.slice(0, match.index) + replacement.apply(this, match);
        
        //这样很好,不用像chrome那样还要记录 previous 的状态,直接把要正则匹配的字符串截断。
        string = string.slice(match.index + match[0].length);
        
        //全局不全局只有这一个差别了
        if (!global) break;
      }
      
      //最后一次匹配结束位置到字符串结尾
      return result + string;
    }
    return _String_replace.apply(this, arguments);
  };

?

?

比较:

chrome : 很大气,很java,效率很高代码基本没有利用javascript的特性,就是用java思路写javascript,

dean.edwards : 很精巧,充分利用了javascript,减少了记忆状态,并且全局不全局统一处理代码很紧凑(compact), 但是string生成比较多哦(slice ,+ 操作)

?

?

Bonus :

?

最简约的实现:(当然replacement不能是函数 ,简约么)

?

String.prototype.replace = function(pattern, replacement) {
     //实现split先...
     return this.split(pattern).join(replacement);
};

? ?

?

1 楼 jiazhigang 2009-08-21  
好大一个黑眼圈
2 楼 lucane 2010-10-23  
@yiminghe
你好,我想请教你点问题,在Chrome中,我是拿的Chromium4.0做的实验
typeof(/a/) 返回的是function
但是typeof(/a/) == "function"却又是false
而typeof(/a/) == "object"又是true

我看到网上有人说,有些JS内核把正则表达式认为是Callable的,所以typeof就返回了function
但是它typeof(/a/) == "function" 为false,这一点再哪有说明吗?
谢谢
3 楼 yiminghe 2010-10-23  
@lucane

我这里chrome8 返回是 function 类型,firefox 是 object
这个typeof 确实有点奇怪,不过一般还是推荐用 Object.prototype.toString来判断
都是 [object regexp]

callable 好像是精粹那本书说的,有一定道理,可能chrome v8引擎对正则表达是编译成函数来进行后面匹配了,但是这取决不同引擎,好像也没有规范

关于为什么不一样,则要看chrome引擎了(注意墙):

http://www.chromium.org/


我也是一直没有找到入门方法,你可以试试,有结果告诉我

4 楼 lucane 2010-10-23  
@yiminghe
谢谢你这么晚还回答了我的问题
我也是偶然看到这个东西,但找了一段时间却没有发现为什么的
要是有结果一定告诉你
  相关解决方案