`
shake863
  • 浏览: 661128 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

prototype 剖析(2)

    博客分类:
  • JS
阅读更多

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方法克里化方法。用于动态的传递参数。比如
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
<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是一致的)看个示例吧。
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
<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,如果是这样
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
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函数)等方法。

首先, 先看普通的实现方法, 它将会有一些限制.
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
<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中直接而显式的编码代码
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
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)另外一种用法,从而改变了所有可列举对象接口的实现。
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
<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方法。比如:
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
//原始的
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原型时,几乎每一个扩展原型的实现,都是一个闭包,且。该扩展是可任意组合的。比如克里化可以有这样的调用方法(上文中已经提到了此方法的使用):
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
<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是最有趣的地方之一,也是实现改变上下文的关键所在。举个简单的例子:
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
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
Copy Code(拷贝代码)-Run HTML(运行代码)-Save Code(另存代码)
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这样的东西还是少用为妙。

分享到:
评论

相关推荐

    Spring实战之Bean的作用域singleton和prototype用法分析

    本篇文章将深入探讨两种主要的作用域:singleton和prototype,并通过实例分析其用法和注意事项。 首先,`singleton`是Spring默认的作用域,意味着当Spring容器初始化时,对于每个具有`singleton`作用域的Bean,它只...

    prototype1.6.0prototype1.6.0prototype1.6.0prototype1.6.0

    标题中的"prototype1.6.0"多次提及,暗示了我们正在讨论JavaScript库Prototype的1.6.0版本。...通过查看和分析这些内容,可以深入理解Prototype 1.6.0的API、方法和特性,以及它如何提升前端开发的效率和代码质量。

    prototype 1.3 源码解读

    通过以上对 Prototype 1.3 源码的分析可以看出,Prototype 旨在通过简洁高效的 API 来简化前端开发工作,尤其是针对 DOM 操作、事件处理以及类型操作等方面。这些核心功能的实现不仅提高了开发效率,还增强了代码的...

    使用Prototype框架.pdfprototype源码分析.doc

    例解Prototype框架.doc 第21章__使用Prototype框架.pdf Prototype源码注释版.pdf proprototype源码分析.doc totype_1.3_源码解读.txt

    prototype1.7.2

    2. **对象扩展与类模拟** Prototype 引入了类的概念,通过模拟类继承来扩展 JavaScript 的原型链。`Class.create()` 方法允许开发者创建新的类,而 `Object.extend()` 方法则用于对象属性的继承。 3. **DOM 操作**...

    prototype学习笔记

    在JavaScript中,`prototype`是一个核心...总的来说,这篇"prototype学习笔记"深入讲解了JavaScript中的原型和继承机制,结合源码分析和实用工具,帮助开发者更全面地掌握这一核心概念,并能够在实际项目中灵活运用。

    prototype-AJAX案例

    通过分析案例代码,你可以深入理解Prototype如何简化AJAX开发,并学会如何在实际项目中应用。 此外,Prototype还提供了其他辅助方法,如`Ajax.activeRequestCount`来跟踪当前活跃的AJAX请求数量,以及`Ajax.options...

    prototype.js

    4. **实例分析**:通过实际案例展示Prototype.js在网页应用中的应用。 5. **最佳实践**:分享一些使用Prototype.js进行开发的经验和技巧。 三、Prototype.js的应用场景 Prototype.js广泛应用于网页动态效果、表单...

    Prototype Carousel Class-图片走马灯

    2. **DOM操作**: Prototype库提供了一套方便的DOM操作API,如`$`选择器、`$$`多元素选择器、`Element.Methods`等,这些方法使得在JavaScript中操作DOM元素变得简单易行。 3. **类(Class)系统**: Prototype支持...

    prototype自己做的一个例子

    在JavaScript中,`prototype`是一个非常重要的概念,它关乎到对象继承和函数原型。...在压缩包`myJs`中,可能包含了实现上述概念的示例代码,通过分析和学习这些代码,可以进一步巩固对`prototype`的理解。

    Prototype Tab图片滚动组件

    2. **事件处理**:Prototype提供了丰富的事件处理机制,如`Event.observe`用于监听事件,`Event.stop`用于阻止事件冒泡。在图片滚动组件中,可能会监听用户的点击或滑动事件,触发图片的切换。 3. **动画效果**:...

    K-prototype源代码

    采用MATLAB编写,根据原作者论文的思路编写,代码里面存在注释,适合聚类分析学习者查看

    prototype-1.4.0源码解读.js

    通过分析Prototype 1.4.0的源码,我们可以更深入地理解JavaScript的设计模式和最佳实践,提升我们的编程技巧。尽管随着技术的发展,新的库和框架层出不穷,但Prototype的许多设计理念和方法仍被广泛借鉴,对学习和...

    JS Ajax XML 处理 (prototype)

    本篇文章将深入探讨如何在Prototype中处理XML数据,以及相关的源码分析。** **一、Prototype库中的Ajax基础** Prototype是一个流行的JavaScript库,它为JavaScript提供了许多实用的工具和扩展,包括Ajax功能。在...

    prototype ajax提交大数据

    2. 打开连接,设置请求的类型(GET或POST),URL以及是否异步。 3. 发送请求,如果是POST请求,需要设置请求头`Content-Type`和请求体。 4. 监听状态变化,当状态变为4(表示响应完成)时,读取服务器返回的数据。 ...

    编写基于Prototype的Javascript动画类

    通过分析这些文件,我们可以更深入地理解基于Prototype的JavaScript动画实现,并可能从中获得灵感,创建出自己的动画库或者改进现有的动画效果。同时,这也会帮助我们更好地理解和运用Prototype库,提升我们的...

    Prototype1.6.0在IE8 9下的问题

    2. **浏览器兼容模式**:IE8和9具有不同的文档模式,包括Quirks模式、IE7标准模式和IE8标准模式。确保浏览器运行在正确的文档模式下是解决兼容性问题的关键。 3. **JavaScript的ECMAScript版本**:IE8默认支持的是...

    prototype进度条的实现例子--progressBar

    在JavaScript的世界里,Prototype是一个非常重要的库,它扩展了JavaScript的基本对象,提供了许多实用的功能,包括...通过分析这些文件,我们可以更深入地了解和学习Prototype库的使用以及进度条组件的具体实现细节。

Global site tag (gtag.js) - Google Analytics