这一期交流会在腾迅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标准化交流会, 前端技术
脚本对字符串的操作方法不同,有时候能很大程度上影响性能
最新研究的结果表明,在脚本引擎中,拼接有意义的单词的性能要远高于无意义的字符串
因此,下面的方法能大大改善性能
- var s = "abdesafer"; //要拼接复制的无意义字符串
- var times = 500; //拼接500份
-
- function copyStr(s, t){
- var tok = "fool"; //用有意义的字符串进行拼接
- var r = ""; //拼接后的字符串
- for(var i = 0; i < t; i++){
- r += tok;
- }
- r = r.replace(/fool/g, s); //替换
- return r;
- }
-
- var d = new Date();
- copyStr(s, times);
- alert(new Date() - d);
月影 前端技术 愚人节玩笑
http://bbs.51js.com/viewthread.php?tid=86342&pid=602059&page=1&extra=page%3D1#pid602059
要设计一个优秀的函数,基本的原则之一是这个函数的表现要可靠,有时候我们不经意间为了实现一个特性而违背了函数的可靠性,给使用者留下了“坑”,这是不应该的
- function test(){
- var x = 1;
- test = function(){
- return x++;
- }
- return test();
- }
这个函数之所以这么写,作者是想在函数中利用闭包特性定义一个局部静态变量,但是,这个函数是有问题的
- function test(){
- var x = 1;
- test = function(){
- return x++;
- }
- return test();
- }
- var test2 = test; //中招
- alert(test2());alert(test2());alert(test2()); //错了
所以,必须要消除这种“坑“,改用下面的写法:
- var test = (function(){
- var x = 1;
- return function() {
- alert(x);
- x ++;
- }
- })();
最后,总结一下定义带有局部静态作用域的函数范式
- var myFunc = (function(){
- var localVariable = "someValue";
- return function(args...){
- //function body
- }
- })();
月影 前端技术 javascript
先看一段代码:
- Function.prototype.$continuous = function(fn){
- var me = this;
- return function(){
- var currentArgs = Array.prototype.slice.call(arguments, 0, me.length);
- var moreArgs = Array.prototype.slice.call(arguments, me.length);
-
- ret = me.apply(this, currentArgs);
-
- if(moreArgs.length > 0){
- ret = fn.call(this, arguments.callee, ret, moreArgs);
- }
-
- return ret;
- }
- }
这个函数并不复杂,它的作用是包装一个函数,判断它实际调用的参数个数和形参个数,当实际调用的参数个数大于形参个数时,再用一个闭包进行后续操作,这个闭包有3个参数,分别是包装的函数自身、前次调用的返回值,以及多余的参数。
这个简单的函数其实比想象得有用:
- var add = function(x,y){
- return x+y;
- }
add显然只是一个简单的两个数相加的程序,如果想让它支持多个数相加呢?
- function reducer(target, returnValue, moreArgs){
- return target.apply(this, [returnValue].concat(moreArgs));
- }
- add = add.$continuous(reducer);
- var a = add(1,2,3,4,5); //a = 1+2+3+4+5=15
同样的:
- var max = function(x,y){
- return x>y?x:y;
- }
- max = max.$continuous(reducer);
- var a = max(1,2,3,2,1); //a=3
还有别的作用:
- function processor(target, returnValue, moreArgs){
- return [returnValue].concat(target.apply(this, moreArgs));
- }
- var $ = function(id){
- return document.getElementById(id);
- }.$continuous(processor);
- var els = $("a","b","c"); //得到3个elements(返回数组)
最后总结一下:
$continuous本身很简单,但是它可以被reducer或processor作用,也就是说,返回结果可以被push,也可以被reduce,上面的reducer实际上是先push再reduce,后面的processor我改了一个次序,先reduce再push,就能获得截然不同的用途。
不知道通过上面的叙述,大家对脚本库核心的基本设计和函数式(functional)编程的思想方法有没有新的认识。不管怎样,JavaScript灵活多变的特性,总能让前端开发充满乐趣。
月影 前端技术
这里用一个小技巧:|0默认转为数值并取整,因为p和q可能长度不同,有可能会出现p[i]或q[j]成为undefined,|0比parseInt更强,可以自动将undefined的值转为0,这样就不用判断p[i]或q[j]是否为数值了。
可以将/\d{1,N}/g改成N位一次运算,默认是4位一次。
- function bigNumMulti(a,b){
- var p = a.match(/\d{1,4}/g).reverse();
- var q = b.match(/\d{1,4}/g).reverse();
- var f1 = 0;
- var result = "0";
-
- for(var i = 0; i < p.length; i++){
- var f2 = 0;
- for(var j = 0; j < q.length; j++){
- var t = (p[i]|0)*(q[j]|0);
- t += new Array(f1+f2+1).join("0");
- result = bigNumAdd(result, t);
- f2 += q[j].length;
- }
- f1 += p[i].length;
- }
- return result;
- }
- function bigNumAdd(a,b){
- var m = a.split('').reverse();
- var n = b.split('').reverse();
- var ret = [];
- var s = 0;
-
- for(var i = 0; i < a.length || i < b.length; i++){
- var t = (m[i]|0) + (n[i]|0) + s;
-
- ret.push(t%10);
- s = (t/10)|0;
- }
- if(s){
- ret.push(s);
- }
- return ret.reverse().join('');
- }
-
- function bigNumPow(a,b){
- var ret = "1";
- for(var i = 0; i < b; i++){
- ret = bigNumMulti(ret,a.toString());
- }
- return ret;
- }
- document.write(bigNumPow(36,16));
月影 前端技术 javascript
YUI3.x里的代码
- publisher.subscribe('event1', function(){
- Y.log('event 1');
- });
- publisher.before('event1', function(){
- Y.log('before: event 1');
- });
- publisher.after('event1', function(){
- Y.log('after: event 1');
- });
- publisher.fire('event1');
触发event1后,看到的log信息将会是:
event 1
before: event1
default event handler
after: event1
其实这种aop,对于JavaScript来说,还可以考虑将它实现在Function上
- var handler = Function.aspact(function(){
- Y.log('event 1');
- });
-
- publisher.subscribe('event1', handler);
-
- hendler.before(function(){
- Y.log('before: event 1');
- });
-
- handler.after(function(){
- Y.log('after: event 1');
- });
-
- publisher.fire('event1');
before和after是针对function或者在这里特定是对handler的
但不一定要用在publisher还可以用在其它地方
月影 前端技术 aop, javascript
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的情况下没必要为了继承而继承……
月影 前端技术 class, javascript, oop, prototype
Recent Comments