`
longgangbai
  • 浏览: 7238509 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Prototype学习文档下载和教程

阅读更多

1、Type - 类型

1)Object类型。在Prototype的基本类型中,因为js是无类型语言这种无类型,纵观全局,Object提供一些供全局静态调用的一些方法。它们包括:检测,扩展。且没有污染到Object的原型对象。(这一点很重要)

2)String类型。它将String对象原型进行进一步的扩展,扩展的方法都是一些常见的strip, stripTags, truncate之类的,有几个方法值得一提:gsub方法,这个方法用来作一个全局的替换,传入的参数有两个,一个是pattern,为正则表达式,第二个是replacement,要替换的数符串。

3)Array类型。它并无特别之处,只是将一些常用功能进行扩展。

4)Hash类型。并非是JS内置内型,是一个属于Prototype自定义的一种类型,支持json类型的序列化和反序列化

5)Function类型。可以说这是几个类型里扩展得比较特别的地方。
argumentNames。得到函数的形参名称,并返回一个形参(字符串型)的一个数组,主要用于动态的调用。与invoke可以形成一个simple Factory pattern。
bind,通俗的说,bind不会让this指针被劫持。说得理论一些,就是用于绑定上下文。关于这一点,我在下面的Context节中会有详细一些的描述。bindAsEventListener,与bind类似,主要区别在于它传递了event对象。
curry方法克里化方法。用于动态的传递参数。比如

 <script type="text/javascript">
 Function.prototype.curry = function() {
 if (!arguments.length) return this;
 var __method = this, args = Array.prototype.slice.call(arguments,0);
 return function() {
 return __method.apply(this, args.concat(Array.prototype.slice.call(arguments,0)));
 }
 }
 var F=function(){alert(Array.prototype.slice.call(arguments,0).join(' '))};
 F.curry('I').curry('am').curry('never-online').curry('http://www.never-online.net')();
 </script>
 

delay函数延迟执行。
wrap把自身函数进行包装,并把包装好的函数作为形参函数中所传递的第一个参数,简单的说就是自身封装。动态改变的将已包装函数的上下文(包装函数的this与执行时的this是一致的)看个示例吧。

 <script>
 Function.prototype.bind = function() {
 if (arguments.length < 2 && typeof(arguments[0])=='undefined') return this;
 var __method = this, args = Array.prototype.slice.call(arguments,0), object = args.shift();
 return function() {
 return __method.apply(object, args.concat(Array.prototype.slice.call(arguments,0)));
 }
 }
  Function.prototype.wrap = function(wrapper) {
 var __method = this;
 return function() {
 return wrapper.apply(this, [__method.bind(this)].concat(Array.prototype.slice.call(arguments,0)));
 }
 }

var a = {
  b: {
    c: function () {
    }
  },
  
  f: function () {
    alert(this==a.b);
  }
};

a.b.c = a.f.wrap(function (F) {
  F();
});
a.b.c();
</script>

a.f被wrap,在匿名函数中,F参数就是a.f,这个被包装好的函数被赋给a.b.c这个函数。我们最后一行调用了a.b.c这个函数,从源代码里可以看到this原来是属于a这个对象,但根据示例之前所说的,这个wrap会动态的替换this。所谓动态,就是看你是怎么调用的。a.f方法的this被谁替换的答案就是,因为a.f从上面的a.b.c()执行,它的对象就是a.b,如果是这样

var f = a.f.wrap(function (F) {
  F();
});
f();

this就会是window,自然上面运行的结果就是false。这个奥秘就是在返回的闭包里。如果你理解闭包,理解apply,就可以从wrap原型中找到答案了。具体的要用书面来解释的话,还是有太多题外话要说,有不清楚的话就在评论中留言吧。

methodize
将动态的指针this作为参数传入闭包中。这个方法,我不是很清楚它的具体作用。看代码是比较简单的,但为何要这样做我也不得而知。

2、Hack
我把这个词从css hack中取过来,暂且放到这里,意指,灵活运用js机制从而实现简化代码的作用。

1) 先不说这个Hack的具体内容,我们不妨思考一个问题,在可列举型的对象上,如:Array, Object等对象,如何实现一个迭代器接口,即each, 利用each实现all(并在每次迭代中执行函数,如果执行函数返回false,则跳出each迭代,也跳出all函数)等方法。

首先, 先看普通的实现方法, 它将会有一些限制.

<script>
/**
 * 如上文所说,先实现each,参数有一个fn,
 * 用apply去动态的调用这个函数并给予形参value还有index
 *
 * @method each
 * @param {function}
 * @return void
 */
Array.prototype.each = function (fn) {
  var self = this; var i = this.length;
  while(i--) fn.apply(null,[self[i],i]);
}

/**
 * 但在all的实现中就不好实现了
 * 因为each是循环的执行某一函数,
 * 不论函数何值都会执行到最后一个元素
 * 因此我们不能够从函数中安全的返回
 *
 * @method each
 * @param {function}
 * @return void
 */
Array.prototype.all = function (fn) {
  this.each(
    function (value, index) {
      if (false===fn.apply(null,[value,index])) {
        return false;
      }
      alert('this is a test for outer Function run count as ' +(value));
      //执行为false时已经将这层函数返回,因此这里将会出现9个alert,很明显不符合我们预期(预期是执行为false时将all函数也返回)
    }
  );
};
var arr = [1,2,3,4,5,6,7,8,9,10];
arr.all(function (value, index) {
  var result = value==6?false:true;
  return result;
});
</script>


从上面的例子来看,我们为什么不在all中单独的实现呢,而是调用each来实现? 当然, 我们可以在all中直接而显式的编码代码

Array.prototype.all = function (fn) {
  var self = this; var i = this.length;
  while(i--) if (!fn.apply(null,[self[i],i])) return;
};


现在再来解释为何不单独实现. 我们可以单独的把each作为一个迭代器, 它是抽象的, 设计模式中说:一个类可以扩展,但不要修改它.另外一点,用each来实现all(当然可以是其它的), 可以节省很多代码

下面看Prototype实现, 用throw 。这个throw,通常用于抛出一个异常给上层代码,让上层代码捕获并加以处理。Prototype给了我们(无论是否是其发明的这种Hack)另外一种用法,从而改变了所有可列举对象接口的实现。

<script>
var $exception = '$$EXCEPTION$$';
Array.prototype.each = function (fn) {
  var self = this; var i = this.length;
  try { while(i--) fn.apply(null,[self[i],i]); }
  catch (e) { if (e!=$exception) throw e; }
}
Array.prototype.all = function (fn) {
  this.each(
    function (value, index) {
      if (false===fn.apply(null,[value,index])) {
        throw $exception;
      }
      alert('this is a test for outer Function run count as ' +(value));
      //执行为false时已经将这层函数返回,因此这里将会出现9个alert,很明显不符合我们预期(预期是执行为false时将all函数也返回)
    }
  );
};
var a = [1,2,3,4,5,6,7,8,9,10];
a.all(function (value, index) {
  var result = value==6?false:true;
  return result;
});
</script>


这个Hack也利用了异常机制, 在要返回自身的上层函数时,抛出异常,在each时捕获。从而可以用each来all。

2)extend。最原始的目的,为了扩展对象方法或属性而实现的一个函数,它是Object的静态方法。这里我们讲述的是Hack方法。比如:

//原始的
var defaultConfig = {
  duration: 72,
  delay: 10
}
function jsclass (objConfig) {
  objConfig.duration = defaultConfig.duration||72;
  objConfig.delay = defaultConfig.delay||10;
  return objConfig;
}
//Hack版
var defaultConfig = {
  duration: 72,
  delay: 10
}
function jsclass (objConfig) {
  Object.extend(objConfig||{}, defaultConfig);
  return objConfig;
}

上面的代码可以看出,用extend可以进行一个简单的验证过程。从而简化了代码。

三、闭包 Closure
在Prototype中,闭包随处可见。最常见的就是在扩展Function原型时,几乎每一个扩展原型的实现,都是一个闭包,且。该扩展是可任意组合的。比如克里化可以有这样的调用方法(上文中已经提到了此方法的使用):

<script>
 Function.prototype.curry = function() {
 if (!arguments.length) return this;
 var __method = this, args = Array.prototype.slice.call(arguments,0);
 return function() {
 return __method.apply(this, args.concat(Array.prototype.slice.call(arguments,0)));
 }
 }
var F = function () { alert(Array.prototype.slice.call(arguments,0).join(' ')); }
F.curry('I').curry('am').curry('never-online')();
</script>

当然,bind, wrap这些都是返回闭包。因为使用闭包,js才更吸引人,也正是因为使用闭包,js才更灵活,可以实现很多在传统编程里意想不到的东西。但反思后会发现,闭包的滥用会导致debug的困难,可读性差。这个问题留到后面再来讨论。

四、上下文 context
在我的理解中,上下文简单的说就是指针。在js的动态特性中,用apply和call是最有趣的地方之一,也是实现改变上下文的关键所在。举个简单的例子:

var F = function () {
  this.name = 'never-online';
  this.alert = function () {
    alert(this.name);
  }
  return this;
}

var C = function () {
  this.name = 'Rank';
  this.alert = function () {
    alert(this.name);
  }
  return this;
}

var oF = new F();
var oC = new C();

关于改变context在很多时候是有用的。这里就不再赘述一些代码的例子了。

五、Lazy function pattern
有人称为惰性函数模式。貌似很理论化的东西,事实上看下面一个例子就明白了,注意看在getXMLHttpRequest里执行了几次alert

var browser = {
  ie: !!window.ActiveXObject,
  ie7: /msie\s*?7\.0/i.test(window.navigator.userAgent)
}
var getXMLHttpRequest = function () {
  if (browser.ie && !browser.ie7)
    getXMLHttpRequest = function () {
      return new ActiveXObject('Microsoft.XMLHTTP');
    }
  else
    getXMLHttpRequest = function () {
      return new XMLHttpRequest();
    }
  alert('看看在getXMLHttpRequest里执行了几次alert');
  return getXMLHttpRequest();
}
var req = getXMLHttpRequest();
alert(req)
var http = getXMLHttpRequest();
alert(http)

上面的在getXMLHttpRequest里只执行了一次alert,由此可以看出,通过js的动态重写函数,达到兼容的目的。第一次调用后,重写自身。动态的重写这是js里又一个很有意思的地方。

我的体会是,Prototype的设计上考虑得很全面。包括层次,接口,模式都用得很到位。这一点是我觉得Prototype优雅的地方,新颖的语法是一大亮点(虽然现在都知道他的$, Try These extend...),它借鉴了许多语言的许多语法,包括Ruby,python等。
Prototype里的相互依赖太紧密。藕合太高,也许Prototype的团队开发的想法是把整个Prototype看作是一个大module(这个粒度是不是有点大-_-!)。闭包大量的使用,调试是一个问题(也许是我水平有限,Prototype的实现某些方法有点“丑陋”),闭包毕竟是不可大量使用的。所以使用时需要注意。因为把可列举对象抽象Enumerable。实现接口_each。用_each来实现each。再用each来实现各集合的方法。把迭代器封装,造成的缺点是让使用者无需考虑迭代细节,如果数据量大,建议不要用Prototype的迭代器。上文中的lazy function pattern这样的东西还是少用为妙。

wrap方法挺好用的,可以用来对现有的方法进行改造及增强,如对getDay进行增强,自定义一周的起始时间

methodize可以理解为预先将this作为第一个参数填好了,以后就可以只填后面的参数,在Element中用得很多

addClassName: function(element, className) {
if (!(element = $(element))) return;
if (!element.hasClassName(className))
element.className += (element.className ? ' ' : '') + className;
return element;
},


prototype中的each感觉还是很不爽,宁可用for~



这篇文章不是用来说明怎么用prototype,不是教程,而是作为一个理论研究,看网络上都是写Prototype的使用,而没有多少人去解释这些设计与实现,给予后来的人以提示和学习。感觉实在是有必要写写了。但一直没有机会写,一来是觉得自己的水平还欠缺,二来是没有太多的时间来写文,三是表述起来比较困难,因为毕竟在框架中交互实现的地方太多,设计依赖也有很多。很难定下来如何描述谁先谁先。如何将这些设计与实现描述出来,且让人容易理解我在说什么,能够不将整个框架说得过于主观,这些都是比较头疼的问题。这次能有这一个机会来让我描述一下这个框架的设计,纯属偶然,没有太多时间来准备,因此,我没有写过多的细节,如果有遗漏之处,还请指出。

概要
Prototype是一个优雅的框架,在何处优雅,它带给我们什么启示?为何我们要学习它?下面我将从Type(类型),Hack,Closure(闭包),Context(上下文),Lazy function pattern(惰性函数模式)和Design(设计)客观的看来prototype。

总览、Design设计
假设你已经明天了怎么样去实现模块,了解了上面Prototype的实现,如果让你来编写一个框架,是否你能够胜任呢?是否能够写得比Prototype好呢?很抱歉,这个问题我不能回答。不管答案怎么样,学习一下总没坏事,下面我们来讨论一下Prototype的是如何将他的代码组织起来的。

1)Object. 这是Prototype实现检测和扩展的核心,全部是静态方法。它提供了全局范围内的验证。
2)Enumerable. 它是全部可列举对象的核心所在。比如: Array, Hash, Object, Element对象的迭代实现。 他们都要实现一个接口。_each方法,每个对象的each都是通过本对象中实现的each来实现的。以下图可以大概表示他们的层次关系
 


其它的对象的迭代实现都是扩展自Enumerable这个对象。_each方法是接口,所有的对象都要实现这个方法,Enumerable则象是基类对象。

3)inheritance. 在新版Prototype中,实现了新的Class.Create的方式,是代替原来的extend object方案,实现起来复杂得多,大量动态的调用了其它类型方法。
主要思路是用原型继承的方式实现继承,但在实现中用了N个闭包。N>=3层。包括实现$supper这个方法,与传统OO不同的是,这个$supper方法,是作为参数传入子类中(且为第一个参数)才有效。(Prototype的方法是基于Alex Arnell写的plugin)
4)Template. 模板类提供了基于json数据的调用。比如一个字符串用Template可以作

var s = "#{name}'s website is #{uri}";
var a = new Template(s);
var o = { name: 'never-online', uri: 'http://www.never-online.net/blog' };
a.evaluate(o);
//得到 never-online's website is http://www.never-online.net/blog

而这个evaluate方法依赖String中的原型方法gsub。

5)Selector. 基于yui-ext的selector。这个的思路比较多,只是大略的了解了DOMquery(yui-ext)的思路,就是compileMatch里采用组装字符串,形成function,动态解析selector。几个selector都是一点是一样的,都有一个cache数组存储临时数据。
6)Event. 在新版Prototype中,有一句评论是这么说的。从原来最差的Event模式,到了最好的Event模式。管理事件都是基于可列举对象的基类,比如在用于事件中的pluck,without等,在实现Event事件里,用了不少的"private"方法,而最后返回的只有几个方法,包括observe, fire和stopObserving。我们也可以简洁的用图来表示

 



7)Ajax. 大概的图就是下面这样,感觉实现得还不错的(对于扩展来说)

 



8)Chaining stuff. 这个在jquery上面用得是最多的,当时jquery出现,推广该框架的时候就是用鼓励和使用Chaining, 这也成为jquery的代表了.
实际上Chaining就是一个循环. chaining是很有用的, 它可以用一种自然语言的方式来编程,而非命令式编式,但会消耗效率,因为迭代器使用过多.Prototype也用了部分链,这个链是从Element开始的,详细的实现我就不说了,原理也是很简单的:
$(id).observe('onclick',handler)就是一个例子,将Event继承到Element上即可.

分享到:
评论
1 楼 pythoner126com 2012-08-28  
国内对prototype热情不是很高,写得不错,有点长,我也是在外单时,客户要求的我才认识这玩意儿。我最近也有翻译,请多多指导:http://www.yiibai.com/prototype
相互学习,共同进步。

相关推荐

    Prototype.js 161JS文件和中文说明书.rar

    API 参考 欢迎使用 Prototype API 参考手册。...这份 API 文档只是一份参考手册,如果你想要学习如何使用 Prototype,或者想熟悉 Prototype 的某些特性,请参考教程。 非常高兴能够制作这份文档!

    prototype教程+ lib

    万一你没有使用过大名鼎鼎的...和在我以前使用这个类库的不少开发者一样,一开始,我不得不一头扎进阅读 prototype.js 的源代码和实验它的功能中。我想,在我学习完它之后,把我学到的东西分享给大家是件不错的事。

    Prototype 1.6.0.3中文参考手册chm.rar

    这份 API 文档只是一份参考手册,如果你想要学习如何使用 Prototype,或者想熟悉 Prototype 的某些特性,请参考教程。 非常高兴能够制作这份文档! 关于这份文件 CHM 文件制作: Remigijus Jodelis. 最后更新...

    Prototype1.6中文手册

    Prototype1.6中文手册欢迎使用 Prototype API 参考手册。... 我们尽力为你提供最新的、叙述清楚的...这份 API 文档只是一份参考手册,如果你想要学习如何使用 Prototype,或者想熟悉 Prototype 的某些特性,请参考教程。

    prototype Api 1.5

    欢迎使用Prototype API...如果你需要学习怎样使用Prototype或者想了解特定属性方面的知识,你可以看看相关的条目。你可以在每一页的上面的访问它('提示与教程'链接),并且也与橙色条相链接,在每个参考页面上都有。

    WEB开发必备几乎包含所有参考资料

    ppt),Jquery1.2.6源码分析,Linux常用命令全集,linux入门文档,MySQL中文参考手册,PHP_MySQL教程,PHP的一些例程,PHP的一些例程,prototype,Spring+in+Action中文版,SQL Server精华 (CHM),Struts快速学习指南,Validato...

    how-to-create-an-interactive-prototype-with-marvel:Tuts+ 教程的练习文件

    Tuts+ 教程:如何使用 Marvel 创建交互式原型讲师:阿曼多... 在本教程中,我们将了解如何首次使用 Marvel,创建手机游戏原型并学习在不同屏幕和选项之间导航的过程。 Tuts+ 教程的源文件: 2014 年 12 月 Tuts+ 上可用

    jQuery详细教程

    您将在本教程下面的章节学习更多有关 callback 参数的知识。 实例 $("button").click(function(){ $("p").hide(1000); }); 亲自试一试 &lt;script type="text/javascript" src="/jquery/jquery.js"&gt;&lt;/script&gt; $...

    Java Web开发实例大全(基础卷) 完整pdf扫描版[179MB]

    《Java Web开发实例大全(提高卷)》既适合Java Web程序员参考和查阅,也适合Java Web初学者,如高校学生、软件开发培训学员及相关求职人员学习、练习、速查使用。 目录 第1篇 流行组件应用篇 第1章 操作XML文件 第...

    《国外写的,翻译版本》设计模式

    6.3 书写文档和学习的辅助手段 232 6.4 现有方法的一种补充 233 6.5 重构的目标 233 6.6 本书简史 234 6.7 模式界 235 6.8 Alexander 的模式语言 235 6.9 软件中的模式 236 6.10 邀请参与 237 6.11 临别感想 237 ...

    Java Web开发实例大全

    《Java Web开发实例大全(提高卷)》既适合Java Web程序员参考和查阅,也适合Java Web初学者,如高校学生、软件开发培训学员及相关求职人员学习、练习、速查使用。 目录 第1篇 流行组件应用篇 第1章 操作XML文件 第...

    asp.net知识库

    帮助解决网页和JS文件中的中文编码问题的小工具 慎用const关键字 装箱,拆箱以及反射 动态调用对象的属性和方法——性能和灵活性兼备的方法 消除由try/catch语句带来的warning 微软的应试题完整版(附答案) 一个...

    java呼叫中心系统源码-introrx-chinese-edition:你错过的React式编程入门--中文版

    刚开始学习时,我试过去找一些教程,并找到了为数不多的实用教程,但是它们都流于表面,从没有围绕RP构建起一个完整的知识体系。库的文档往往也无法帮助你去了解它的函数。不信的话可以看一下这个: Rx.Observable....

    《设计模式可复用面向对象软件的基础》

    2326.1 设计模式将带来什么 2326.2 一套通用的设计词汇 2326.3 书写文档和学习的辅助手段 2326.4 现有方法的一种补充 2336.5 重构的目标 2336.6 本书简史 2346.7 模式界 2356.8 Alexander 的模式语言 2356.9 软件中...

    libusb-1.0.9

    libusb1.0学习(一) 首先声明,这是看到国外论坛上的学习文章后,独立翻译过来作为笔记用,加入部分自我理解,并且全部原创。 介绍: libusb是一个开源库,可以帮助开发者在用户空间的层面上与UBS设备进行通讯。...

Global site tag (gtag.js) - Google Analytics