Archive

Archive for the ‘前端技术’ Category

我和JavaScript的那些事儿 —— 记Web标准化交流会2010.8

August 30th, 2010

这一期交流会在腾迅soso举行。本期交流会的主题是我建议裕波的,叫做“我和JavaScript的那些事儿”,主要是希望借着交流会交流为主的特色,让大家谈谈在学习JavaScript过程中的经历和自己对使用和认识JavaScript的一些心得。

我自己是05年才开始接触JavaScript的,在这之前,接触前端的东西不多,虽然也写过perl和php的小网站,但原先我更多是做做单片机以及一些桌面软件的,所以用C语言、C++和C#比较多。对JavaScript,最初的认识我和一些同学一样,认为它是一种简单的不完善的脚本语言,不值得深入研究。而当时早期的互联网前端确实也对JavaScript研究不多,大部分网上的介绍和书籍都把JavaScript描述为在页面上增加一些特效的辅助脚本,这加深了人们对JavaScript的误解。04年毕业后到了深圳去之后,因为工作的关系,用Java。当时我们做信息系统,是web的,需要写一些前端的东西,当时团队里面没有做前端的人,所以我只好自己去研究,所以从05年开始我自己慢慢接触了JavaScript。当时主要是通过看书和在51js上和别人交流来学习JavaScript的。我搜了一下我在51js上发表的文章,最早的是2005年4月22日的,差不多我也就是从那时候开始真正接触的前端开发。

我对脚本的认识和研究一开始是从别的语言入手的。客观上因为当时我所在的团队没有专业的前端工程师,大家对Java和一些后端语言比较熟悉,对JavaScript没有什么认识,所以为了让大家能够顺利使用这种脚本语言,我当时的思路是去研究如何用JavaScript去模拟别的语言的特性。因此我在51js上发表的第一篇文章是对javascript重载的深入探讨,这是一种语言对另一种语言特性的模拟。从一开始,我就是专门从语言特性入手对JavaScript进行学习和研究的,这对我以前的经历来说是最佳的学习方式,但是,在很长一段时间内这仅仅是针对语言的研究,甚至不能算是前端的东西,JavaScript在这里只是一种特定的语言工具而已,而我研究的目的,仅仅是为了让不懂JavaScript的同事能够很方便地上手使用这门语言。由于信息系统的浏览器兼容性要求不高,因此对浏览器的研究,是在我经历了很长一段时间的实践之后才慢慢开始的。直到现在为止,浏览器兼容这一块也还是我的弱项。

要用一种语言很好地模拟另一种语言,必须对它的语言特性非常了解,随着我对脚本特性研究的深入,我发现我喜欢上了这种以原型(Prototype)为基础的语言,相对于类继承来说,原型在轻巧和严谨之间保持了一种微妙的平衡,我称之为“优雅”。对JavaScript的了解越深入,我对它的特性越喜欢,我发现它的模拟能力非常强大,不但能模拟类继承的面向对象语言,也能模拟出其他你想要的语法特性,例如这一篇我在51js上发表的文章——令人惊讶的用法…用Js模拟C#的Attribute ^^。我将对语言特性的模拟研究得比较深之后,将想要的特性集中起来,慢慢形成一个脚本框架,即后来的Silvern的第一版。这个框架的类库主要是模拟了别的语言的特点,实现了面向对象继承,并且用一个基本模式将数据和页面过程规范起来,让开发者能够以他们所熟悉的后端语言类似的方式来完成开发。这个框架当时是我独立构思的,除了后期参考了jsvm的一些思路之外,没有看过别的框架。而这个框架同现在业内成熟的一些脚本框架来比较,出发点也是很不同的,它主要的目的依然是让后端人员用他们熟悉的方式来写前端代码,因此不是一个现在意义上完整的前端框架,除了必要的Ajax封装之外,它对于浏览器差异等等纯前端的问题几乎都没有处理。后来的一段时间内我在实践中完善这个框架,在后续版本中增加了“元类”这个概念,让它变得更加动态。而正是因为研究“元类”我对闭包这类概念有了比较深入的认识,也开始研究functional。

Silverna第一版大概到06年中的时候停止开发,第一个原因是它已基本满足当时的开发需求,第二个原因是随着我慢慢对前端开发有了更加深入的了解后,我基本放弃了用JavaScript模拟后端的想法,开始寻求真正的前端开发解决之道。可以说到那时候起,我才算真正入了前端的门。

此时此刻,我回想起JavaScript的学习历程,几年前的一些记忆还很清晰,可以说通过学习JavaScript这门优秀的语言我得到了很多东西,也可以说是JavaScript引导我进入前端开发这个行业的。感谢JavaScript,感谢前端开发,带给我快乐,也让我有今天的成就。

———-以上是一些个人感慨,我是分割线———-

在交流会上除了交流对JavaScript学习的经历之外,还有不少牛人给我们做了精彩的分享。感谢曾哥、感谢阿当、感谢rank、感谢因为迟到我没有听到分享的吕婷美女:P。

感谢腾迅soso送给我们的QQ仔,我居然带了两大两小4只回家 =.= ,其中有一只小的应该是拿错了坐在附近的别的同学的,希望不要责怪我,汗。。。。。。

不知不觉,web标准化交流会已经快要迎来一岁生日。感谢裕波和组办者们为了交流会投入的时间和精力,也感谢所有为交流会提供场地的公司。在参加的每一期交流会里,我都能收获很多,希望交流会越办越好。

月影 Web标准化交流会, 前端技术

提升javascript程序字符串操作的性能

April 1st, 2010

脚本对字符串的操作方法不同,有时候能很大程度上影响性能
最新研究的结果表明,在脚本引擎中,拼接有意义的单词的性能要远高于无意义的字符串
因此,下面的方法能大大改善性能

  1. var s = "abdesafer"//要拼接复制的无意义字符串
  2. var times = 500; //拼接500份
  3.  
  4. function copyStr(s, t){
  5.     var tok = "fool"; //用有意义的字符串进行拼接
  6.     var r = ""; //拼接后的字符串
  7.     for(var i = 0; i < t; i++){
  8.         r += tok;
  9.     }
  10.     r = r.replace(/fool/g, s); //替换
  11.     return r;
  12. }
  13.  
  14. var d = new Date();
  15. copyStr(s, times);
  16. alert(new Date() - d);

月影 前端技术

函数的可靠性

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. })();

月影 前端技术

神奇的“连续”

August 31st, 2009

先看一段代码:

  1. Function.prototype.$continuous = function(fn){
  2.     var me = this;
  3.     return function(){
  4.         var currentArgs = Array.prototype.slice.call(arguments, 0, me.length);
  5.         var moreArgs = Array.prototype.slice.call(arguments, me.length);
  6.  
  7.         ret = me.apply(this, currentArgs);
  8.  
  9.         if(moreArgs.length > 0){
  10.             ret = fn.call(this, arguments.callee, ret, moreArgs);
  11.         }
  12.  
  13.         return ret;
  14.     }
  15. }

这个函数并不复杂,它的作用是包装一个函数,判断它实际调用的参数个数和形参个数,当实际调用的参数个数大于形参个数时,再用一个闭包进行后续操作,这个闭包有3个参数,分别是包装的函数自身、前次调用的返回值,以及多余的参数。

这个简单的函数其实比想象得有用:

  1. var add = function(x,y){
  2.     return x+y;
  3. }

add显然只是一个简单的两个数相加的程序,如果想让它支持多个数相加呢?

  1. function reducer(target, returnValue, moreArgs){
  2.     return target.apply(this, [returnValue].concat(moreArgs));
  3. }
  4. add = add.$continuous(reducer);
  5. var a = add(1,2,3,4,5)//a = 1+2+3+4+5=15

同样的:

  1. var max = function(x,y){
  2.     return x>y?x:y;
  3. }
  4. max = max.$continuous(reducer);
  5. var a = max(1,2,3,2,1); //a=3

还有别的作用:

  1. function processor(target, returnValue, moreArgs){
  2.     return [returnValue].concat(target.apply(this, moreArgs));
  3. }  
  4. var $ = function(id){
  5.     return document.getElementById(id);
  6. }.$continuous(processor);
  7. var els = $("a","b","c")//得到3个elements(返回数组)

最后总结一下:
$continuous本身很简单,但是它可以被reducer或processor作用,也就是说,返回结果可以被push,也可以被reduce,上面的reducer实际上是先push再reduce,后面的processor我改了一个次序,先reduce再push,就能获得截然不同的用途。

不知道通过上面的叙述,大家对脚本库核心的基本设计和函数式(functional)编程的思想方法有没有新的认识。不管怎样,JavaScript灵活多变的特性,总能让前端开发充满乐趣。 :)

月影 前端技术

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的情况下没必要为了继承而继承……

月影 前端技术 , , ,