Archive

Posts Tagged ‘javascript’

函数的可靠性

December 21st, 2009

http://bbs.51js.com/viewthread.php?tid=86342&pid=602059&page=1&extra=page%3D1#pid602059

要设计一个优秀的函数,基本的原则之一是这个函数的表现要可靠,有时候我们不经意间为了实现一个特性而违背了函数的可靠性,给使用者留下了“坑”,这是不应该的

  1. function test(){
  2.         var x = 1;
  3.         test = function(){
  4.             return x++;
  5.         }
  6.         return test();
  7.     }

这个函数之所以这么写,作者是想在函数中利用闭包特性定义一个局部静态变量,但是,这个函数是有问题的

  1. function test(){
  2.         var x = 1;
  3.         test = function(){
  4.             return x++;
  5.         }
  6.         return test();
  7.     }
  8.     var test2 = test; //中招
  9.     alert(test2());alert(test2());alert(test2()); //错了

所以,必须要消除这种“坑“,改用下面的写法:

  1. var test = (function(){
  2.     var x = 1;
  3.     return function() {
  4.         alert(x);
  5.         x ++;
  6.     }
  7. })();

最后,总结一下定义带有局部静态作用域的函数范式

  1. var myFunc = (function(){
  2.     var localVariable = "someValue";
  3.     return function(args...){
  4.         //function body
  5.     }
  6. })();

月影 前端技术

JavaScript的大数加法、乘法和乘方

June 2nd, 2009

这里用一个小技巧:|0默认转为数值并取整,因为p和q可能长度不同,有可能会出现p[i]或q[j]成为undefined,|0比parseInt更强,可以自动将undefined的值转为0,这样就不用判断p[i]或q[j]是否为数值了。
可以将/\d{1,N}/g改成N位一次运算,默认是4位一次。

  1. function bigNumMulti(a,b){
  2.     var p = a.match(/\d{1,4}/g).reverse();
  3.     var q = b.match(/\d{1,4}/g).reverse();
  4.     var f1 = 0;
  5.     var result = "0";
  6.  
  7.     for(var i = 0; i < p.length; i++){
  8.         var f2 = 0;
  9.         for(var j = 0; j < q.length; j++){
  10.             var t = (p[i]|0)*(q[j]|0);
  11.             t += new Array(f1+f2+1).join("0");
  12.             result = bigNumAdd(result, t);
  13.             f2 += q[j].length;
  14.         }
  15.         f1 += p[i].length;
  16.     }
  17.     return result;
  18. }
  19. function bigNumAdd(a,b){
  20.     var m = a.split('').reverse();
  21.     var n = b.split('').reverse();
  22.     var ret = [];
  23.     var s = 0;
  24.  
  25.     for(var i = 0; i < a.length || i < b.length; i++){
  26.         var t = (m[i]|0) + (n[i]|0) + s;
  27.  
  28.         ret.push(t%10);
  29.         s = (t/10)|0;
  30.     }
  31.     if(s){
  32.         ret.push(s);
  33.     }
  34.     return ret.reverse().join('');
  35. }
  36.  
  37. function bigNumPow(a,b){
  38.     var ret = "1";
  39.     for(var i = 0; i < b; i++){
  40.         ret = bigNumMulti(ret,a.toString());
  41.     }
  42.     return ret;
  43. }
  44. document.write(bigNumPow(36,16));

月影 前端技术

抽奖和洗牌算法

May 26th, 2009
  1. function playCard(cardType, cardNum){
  2.     var cards = [];
  3.     for(var i = 0; i < cardType.length; i++){
  4.         cards.push.apply(cards, new Array(cardNum[i]+1).join(cardType[i]).split(''));
  5.     }
  6.     for(var i = 0; i < 10; i++){  //可以多洗几次牌
  7.                cards.sort(function(){return Math.random()>0.5?1:-1});
  8.         }
  9.         return cards; //洗好的牌
  10. }
  11.  
  12. var cardType = ['A','B','C','D','E','F','G'];
  13. var cardNum = [28,24,20,15,7,5,1];
  14.  
  15. document.write(playCard(cardType, cardNum));

月影 前端技术

利用闭包实现动态多面骰子系统

May 26th, 2009
  1. function makeDice(values, probPoints){
  2.     var sidesEdge = [0];
  3.  
  4.     for(var i = 0; i < values.length; i++){
  5.         sidesEdge.push(sidesEdge[sidesEdge.length-1]+(probPoints[i]|0));
  6.     }
  7.    
  8.     return {
  9.         thrown:function(){
  10.             var val = (sidesEdge[sidesEdge.length-1] * Math.random())|0;
  11.             for(var i = 0; i < sidesEdge.length; i++){
  12.                 if(val < sidesEdge[i]){
  13.                     break;
  14.                 }
  15.             }
  16.             return values[i - 1];
  17.         }
  18.     }
  19. }
  20.  
  21. var values = ['A','B','C','D','E','F','G'];
  22. var probPoints = [28,24,20,15,7,5,1];
  23. var dice = makeDice(values, probPoints)
  24.  
  25. var result = [0,0,0,0,0,0,0];
  26. for(var i = 0; i < 10000; i++){
  27.     result[dice.thrown().charCodeAt(0)-65]++;
  28. }
  29. document.write("总次数10000,各值出现次数:");
  30. for(var i = 0; i < values.length; i++){
  31.     document.write(values[i]+":"+result[i]+" ");
  32. }

月影 前端技术 ,

两段“邪恶”的代码,模仿需谨慎……

May 21st, 2009
  1. Array.prototype.toggle = function(val){
  2.     return this[(this[0] == val)|0]
  3. }
  4.  
  5. el.className = ["class1","class2"].toggle(el.className);
  1. Array.prototype.switch = function(val){
  2.     return this[(this.indexOf(val) + 1) % this.length];
  3. }
  4.  
  5. el.className = ["class1","class2","class3"].switch(el.className);

月影 前端技术

关于切面

May 20th, 2009

YUI3.x里的代码

  1. publisher.subscribe('event1', function(){
  2. Y.log('event 1');
  3. });
  4. publisher.before('event1', function(){
  5. Y.log('before: event 1');
  6. });
  7. publisher.after('event1', function(){
  8. Y.log('after: event 1');
  9. });
  10. publisher.fire('event1');

触发event1后,看到的log信息将会是:
event 1
before: event1
default event handler
after: event1

其实这种aop,对于JavaScript来说,还可以考虑将它实现在Function上

  1. var handler = Function.aspact(function(){
  2.     Y.log('event 1');
  3. });
  4.  
  5. publisher.subscribe('event1', handler);
  6.  
  7. hendler.before(function(){
  8.     Y.log('before: event 1');
  9. });
  10.  
  11. handler.after(function(){
  12.     Y.log('after: event 1');
  13. });
  14.  
  15. publisher.fire('event1');

before和after是针对function或者在这里特定是对handler的
但不一定要用在publisher还可以用在其它地方

月影 前端技术 ,

OOP随想——prototype-based & class-based

May 6th, 2009

JavaScript是prototype-based的语言,而标准的面向对象语言大多数是class-based的

然而其实对于面向对象来说,prototype-based的思想和class-based的思想一样是完备的
事实上prototype-based的思想是人类更直接地描绘自然世界的方法,它直接利用相似性来“认知世界”
这和人类的思维模式是非常的接近的
新生的儿童是通过相似类比(prototype)来学习和巩固接触到的新知识的,而只有在更高级别上才会对知识进行整理和归类(class)

所以说对prototype-based来说:
A.prototype = new B(); 的现实语言就是“A很像B”,或者“像B那样对待A就行了”
Tiger.prototype = new Cat(); //照猫画虎

对class-based来说,就不一样:
A extends B; 的现实语言是“A是B的一种”
abstract Felidae extends Animal //猫科动物是一种动物
Tiger extends Felidae //老虎是一种猫科动物
Cat extends Felidae //猫是一种猫科动物

区别就在上面,对class-based来说,猫和老虎的关系是(必须)通过抽象的“猫科动物”来建立联系的,猫和老虎重用的是猫科动物的通用行为
而对于prototype-based来说,因为猫本身像老虎,所以这个相似本身就能够让猫建立对老虎的行为重用的可能,至于哪些行为能够重用,交由使用者去处理。

我们认知世界也是同样,既有非常严谨的对世界的归类,也有比较宽松的“相似性”描述,而后者,可能出现得更加频繁。
人脑的“联想”通常是偏prototype-based的,而逻辑思维同时具有class-based(分析)、prototype-based(推理)两种特质。

本质上说,编程是一种把现实世界的模型抽象化后再利用计算机具现化的行为,所以无论是prototype-based还是class-based,都是思维抽象化的手段,在有prototype的情况下没必要为了继承而继承……

月影 前端技术 , , ,

再谈范式

May 5th, 2009

范式:new C <=> TC = function(){}, TC.prototype = C.prototype, o= new TC, C.apply(o, arguments);

用文字和伪代码解释就是
与构造器C构造过程等价的函数调用序列为
1) 建立一个空的function模板TC var TC = function(){}
2) 设这个模板TC的原型设为构造器C的原型 TC.prototype = C.prototype
3) 无参数构造TC的对象o var o = new TC();
4) 以o为this执行C的构造器 C.apply(o, arguments);
5) 得到的o和直接构造C的过程完全等价 o <=> new C;

这个范式有非常大的作用
第一,用作继承时延迟基类构造函数的调用

  1. Function.prototype.$extends = function(p){
  2.     var me = this;
  3.     var T = function(){
  4.         this.$super = p;
  5.         me.apply(this, arguments);
  6.         this.$super = null;
  7.     };
  8.  
  9.     var fn = function(){};
  10.     fn.prototype = p.prototype;   
  11.     T.prototype = new fn();
  12.     T.constructor = me;
  13.  
  14.     return T;
  15. };
  16. function Vector(){
  17.     this.axis = Array.prototype.slice.call(arguments,0);
  18. };
  19. Vector.prototype.dimension = function(){
  20.     return this.axis.length;
  21. };
  22. Vector.prototype.length = function(){
  23.     var len = this.axis.length;
  24.     var ret = 0;
  25.     for(var i = 0; i < len; i++){
  26.         ret += this.axis[i] * this.axis[i];
  27.     }
  28.     return Math.sqrt(ret);
  29. };
  30. Vector.prototype.toString = function(){
  31.     return "[" + this.axis + "]";
  32. };
  33.  
  34. var Vector2D = function(x,y){
  35.     this.x = x;
  36.     this.y = y;
  37.     this.$super(x,y);
  38. }.$extends(Vector);
  39. Vector2D.prototype.m = function(vector){
  40.     return this.x * vector.y - vector.x * this.y;
  41. };
  42.  
  43. var Vector3D = function(x,y,z){
  44.     this.x = x;
  45.     this.y = y;
  46.     this.z = z;
  47.     this.$super(x,y,z);
  48. }.$extends(Vector);
  49.  
  50. var v1 = new Vector2D(1,2);
  51. alert([v1,v1.dimension()]);
  52. alert(v1.m(new Vector2D(3,4)));
  53.  
  54. var v2 = new Vector3D(1,2,3);
  55. alert([v2,v2.dimension()]);
  56. alert(v2.length());

第二,实现构造器和函数调用的统一,让对象构造支持apply和call

  1. Function.prototype.createInstance = function(){
  2.     var T = function(){};
  3.     T.prototype = this.prototype;
  4.     var o = new T();
  5.     this.apply(o, arguments);
  6.     return o;
  7. }
  8.  
  9. function Foo(x,y,z){
  10.     this.x = x;
  11.     this.y = y;
  12.     this.z = z;
  13. };
  14.  
  15. var o = Foo.createInstance();
  16. alert(o);
  17. alert(o.constructor);
  18. alert(o instanceof Foo); //连instanceof都完全正确
  19.  
  20. function test(){
  21.     return Foo.createInstance.apply(Foo, arguments); //轻松使用apply
  22. }
  23.  
  24. var o2 = test(1,2,3);
  25. alert([o2.x,o2.y,o2.z]);

月影 前端技术 , ,