近期在搞Mix2ool,是mix framework的進化版,這次view engine由B/S 轉為C/S架構,大量使用了javascript。

其中寫了這麼的一段

1
2
3
4
5
6
7
8
9
10
11
mix.Bindable.bindingFunc = function (n, old, vari){
	var b = $(this).attr("binding");
	if (b == null) b = "";
	var context = this;
	b.replace(" ", "").split(";").removeNothing().each(function (v){
		var t = v.split(":");
		if (t[1] == vari){
			$(this).attr(t[0], n);
		}
	}, this);
};

就是做binding的工作,然而卻產生了too much recursion的問題,因為當我setAttribute的時候,下一個element也會notify一個attrChange event,那個event同時又做了binding的工作,結果bind得沒完沒了。

解決方法是把$(this).attr(t[0], n)放在一個新的closure中進行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mix.Bindable.bindingFunc = function (n, old, vari){
	var b = $(this).attr("binding");
	if (b == null) b = "";
	var context = this;
	b.replace(" ", "").split(";").removeNothing().each(function (v){
		var t = v.split(":");
		if (t[1] == vari){
			if (mix.Bindable.stacked > mix.Bindable.maxStack){
				//if there are too many stacked event handlers, browsers will stop running due to "too much recursion"
				mix.Bindable.stacked = 0;
				(function(){ //this method is generally slower in performance, so use different methods alternatively
					$(context).attr(t[0], n);
				}).once(0);
			}else{
				mix.Bindable.stacked++;
				$(this).attr(t[0], n);
			}
		}
	}, this);
};

把.attr()放在一個function之中並在0微秒之後執行(setTimeout),可以在這個closure的執行者設為window,而不是mix.Bindable.bindFunc,那就可以解決問題了。然而,如果把所有工作都放到新的function內執行,setTimeout產生出來的delay會很明顯,因此我用了mix.Bindable.stacked來記錄recur了的次數,當recur到一定大的地步才會在新的function執行,這可以加快一點速度。

P.S.其實這個方法不太好,現在我在想其他方法。