prototype.js解读
关键字: prototype.js,解读,注释/*
* 定义一个全局对象, 属性 Version 在发布的时候会替换为当前版本号
*/
var Prototype = {
Version: '1.5.0_rc0',
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
emptyFunction: function()
{
},
K: function(x)
{
return x
}
}
/*
* 创建一种类型,注意其属性 create 是一个方法,返回一个构造函数。
* 一般使用如下
* var X = Class.create(); 返回一个类型,类似于 java 的一个Class实例。
* 要使用 X 类型,需继续用 new X()来获取一个实例,如同 java 的 Class.newInstance()方法。
*
* 返回的构造函数会执行名为 initialize 的方法, initialize 是 Ruby 对象的构造器方法名字。
* 此时initialize方法还没有定义,其后的代码中创建新类型时会建立相应的同名方法。
*
* 如果一定要从java上去理解。你可以理解为用Class.create()创建一个继承java.lang.Class类的类。当然java不允许这样做,因为Class类是final的
*
*/
var Class = {
create: function()
{
return function()
{
this.initialize.apply(this, arguments);
}
}
}
/*
* 创建一个对象,从变量名来思考,本意也许是定义一个抽象类,以后创建新对象都 extend 它。
* 但从其后代码的应用来看, Abstract 更多是为了保持命名空间清晰的考虑。
* 也就是说,我们可以给 Abstract 这个对象实例添加新的对象定义。
*
* 从java去理解,就是动态给一个对象创建内部类。
*/
var Abstract = new Object();
/*
* 获取参数对象的所有属性和方法,有点象多重继承。但是这种继承是动态获得的。
* 如:
* var a = new ObjectA(), b = new ObjectB();
* var c = a.extend(b);
* 此时 c 对象同时拥有 a 和 b 对象的属性和方法。但是与多重继承不同的是,c instanceof ObjectB 将返回false。
*/
Object.extend = function(destination, source)
{
for (var property in source)
{
destination[property] = source[property];
}
return destination;
}
Object.inspect = function(object)
{
try
{
if (object == undefined) return 'undefined';
if (object == null) return 'null';
return object.inspect ? object.inspect() : object.toString();
}
catch (e)
{
if (e instanceof RangeError) return '...';
throw e;
}
}
/*
* 这个方法很有趣,它封装一个javascript函数对象,返回一个新函数对象,新函数对象的主体和原对象相同,但是bind()方法参数将被用作当前对象的对象。
* 也就是说新函数中的 this 引用被改变为参数提供的对象。
* 比如:
* <input type="text" id="aaa" value="aaa">
* <input type="text" id="bbb" value="bbb">
* .................
* <script>
* var aaa = document.getElementById("aaa");
* var bbb = document.getElementById("bbb");
* aaa.showValue = function() {alert(this.value);}
* aaa.showValue2 = aaa.showValue.bind(bbb);
* /script>
* 那么,调用aaa.showValue 将返回"aaa", 但调用aaa.showValue2 将返回"bbb"。
*
* apply 是ie5.5后才出现的新方法(Netscape好像很早就支持了)。
* 该方法更多的资料参考MSDN http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthApply.asp
* 还有一个 call 方法,应用起来和 apply 类似。可以一起研究下。
*/
Function.prototype.bind = function()
{
var __method = this, args = $A(arguments), object = args.shift();
return function()
{
return __method.apply(object, args.concat($A(arguments)));
}
}
/*
* 和bind一样,不过这个方法一般用做html控件对象的事件处理。所以要传递event对象
* 注意这时候,用到了 Function.call。它与 Function.apply 的不同好像仅仅是对参数形式的定义。
* 如同 java 两个过载的方法。
*/
Function.prototype.bindAsEventListener = function(object)
{
var __method = this;
return function(event)
{
return __method.call(object, event || window.event);
}
}
/*
* 将整数形式RGB颜色值转换为HEX形式
*/
Object.extend(Number.prototype, {
toColorPart: function()
{
var digits = this.toString(16);
if (this < 16) return '0' + digits;
return digits;
},
succ: function()
{
return this + 1;
},
times: function(iterator)
{
$R(0, this, true).each(iterator);
return this;
}
});
/*
* 典型 Ruby 风格的函数,将参数中的方法逐个调用,返回第一个成功执行的方法的返回值
*/
var Try = {
these: function()
{
var returnValue;
for (var i = 0; i < arguments.length; i++)
{
var lambda = arguments[i];
try
{
returnValue = lambda();
break;
}
catch (e)
{
}
}
return returnValue;
}
}
/*--------------------------------------------------------------------------*/
/*
* 一个设计精巧的定时执行器
* 首先由 Class.create() 创建一个 PeriodicalExecuter 类型,
* 然后用对象直接量的语法形式设置原型。
*
* 需要特别说明的是 rgisterCallback 方法,它调用上面定义的函数原型方法bind, 并传递自己为参数。
* 之所以这样做,是因为 setTimeout 默认总以 window 对象为当前对象,也就是说,如果 registerCallback 方法定义如下的话:
* registerCallback: function() {
* setTimeout(this.onTimerEvent, this.frequency * 1000);
* }
* 那么,this.onTimeoutEvent 方法执行失败,因为它无法访问 this.currentlyExecuting 属性。
* 而使用了bind以后,该方法才能正确的找到this,也就是PeriodicalExecuter的当前实例。
*/
var PeriodicalExecuter = Class.create();
PeriodicalExecuter.prototype = {
initialize: function(callback, frequency)
{
this.callback = callback;
this.frequency = frequency;
this.currentlyExecuting = false;
this.registerCallback();
},
registerCallback: function()
{
setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
onTimerEvent: function()
{
if (!this.currentlyExecuting)
{
try
{
this.currentlyExecuting = true;
this.callback();
}
finally
{
this.currentlyExecuting = false;
}
}
}
}
Object.extend(String.prototype, {
gsub: function(pattern, replacement)
{
var result = '', source = this, match;
replacement = arguments.callee.prepareReplacement(replacement);
while (source.length > 0)
{
if (match = source.match(pattern))
{
result += source.slice(0, match.index);
result += (replacement(match) || '').toString();
source = source.slice(match.index + match[0].length);
}
else
{
result += source,source = '';
}
}
return result;
},
sub: function(pattern, replacement, count)
{
replacement = this.gsub.prepareReplacement(replacement);
count = count === undefined ? 1 : count;
return this.gsub(pattern, function(match)
{
if (--count < 0) return match[0];
return replacement(match);
});
},
scan: function(pattern, iterator)
{
this.gsub(pattern, iterator);
return this;
},
truncate: function(length, truncation)
{
length = length || 30;
truncation = truncation === undefined ? '...' : truncation;
return this.length > length ?
this.slice(0, length - truncation.length) + truncation : this;
},
strip: function()
{
return this.replace(/^\s+/, '').replace(/\s+$/, '');
},
stripTags: function()
{
return this.replace(/<\/?[^>]+>/gi, '');
},
stripScripts: function()
{
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
},
extractScripts: function()
{
var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
return (this.match(matchAll) || []).map(function(scriptTag)
{
return (scriptTag.match(matchOne) || ['', ''])[1];
});
},
evalScripts: function()
{
return this.extractScripts().map(function(script)
{
return eval(script)
});
},
escapeHTML: function()
{
var div = document.createElement('div');
var text = document.createTextNode(this);
div.appendChild(text);
return div.innerHTML;
},
unescapeHTML: function()
{
var div = document.createElement('div');
div.innerHTML = this.stripTags();
return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
},
toQueryParams: function()
{
var pairs = this.match(/^\??(.*)$/)[1].split('&');
return pairs.inject({}, function(params, pairString)
{
var pair = pairString.split('=');
params[pair[0]] = pair[1];
return params;
});
},
toArray: function()
{
return this.split('');
},
camelize: function()
{
var oStringList = this.split('-');
if (oStringList.length == 1) return oStringList[0];
var camelizedString = this.indexOf('-') == 0
? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
: oStringList[0];
for (var i = 1, len = oStringList.length; i < len; i++)
{
var s = oStringList[i];
camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
}
return camelizedString;
},
inspect: function()
{
return "'" + this.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') + "'";
}
});
String.prototype.gsub.prepareReplacement = function(replacement)
{
if (typeof replacement == 'function') return replacement;
var template = new Template(replacement);
return function(match)
{
return template.evaluate(match)
};
}
String.prototype.parseQuery = String.prototype.toQueryParams;
var Template = Class.create();
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype = {
initialize: function(template, pattern)
{
this.template = template.toString();
this.pattern = pattern || Template.Pattern;
},
evaluate: function(object)
{
return this.template.gsub(this.pattern, function(match)
{
var before = match[1];
if (before == '\\') return match[2];
return before + (object[match[3]] || '').toString();
});
}
}
var $break = new Object();
var $continue = new Object();
var Enumerable = {
each: function(iterator)
{
var index = 0;
try
{
this._each(function(value)
{
try
{
iterator(value, index++);
}
catch (e)
{
if (e != $continue) throw e;
}
});
}
catch (e)
{
if (e != $break) throw e;
}
},
all: function(iterator)
{
var result = true;
this.each(function(value, index)
{
result = result && !!(iterator || Prototype.K)(value, index);
if (!result) throw $break;
});
return result;
},
any: function(iterator)
{
var result = true;
this.each(function(value, index)
{
if (result = !!(iterator || Prototype.K)(value, index))
throw $break;
});
return result;
},
collect: function(iterator)
{
var results = [];
this.each(function(value, index)
{
results.push(iterator(value, index));
});
return results;
},
detect: function (iterator)
{
var result;
this.each(function(value, index)
{
if (iterator(value, index))
{
result = value;
throw $break;
}
});
return result;
},
findAll: function(iterator)
{
var results = [];
this.each(function(value, index)
{
if (iterator(value, index))
results.push(value);
});
return results;
},
grep: function(pattern, iterator)
{
var results = [];
this.each(function(value, index)
{
var stringValue = value.toString();
if (stringValue.match(pattern))
results.push((iterator || Prototype.K)(value, index));
})
return results;
},
include: function(object)
{
var found = false;
this.each(function(value)
{
if (value == object)
{
found = true;
throw $break;
}
});
return found;
},
inject: function(memo, iterator)
{
this.each(function(value, index)
{
memo = iterator(memo, value, index);
});
return memo;
},
invoke: function(method)
{
var args = $A(arguments).slice(1);
return this.collect(function(value)
{
return value[method].apply(value, args);
});
},
max: function(iterator)
{
var result;
this.each(function(value, index)
{
value = (iterator || Prototype.K)(value, index);
if (result == undefined || value >= result)
result = value;
});
return result;
},
min: function(iterator)
{
var result;
this.each(function(value, index)
{
value = (iterator || Prototype.K)(value, index);
if (result == undefined || value < result)
result = value;
});
return result;
},
partition: function(iterator)
{
var trues = [], falses = [];
this.each(function(value, index)
{
((iterator || Prototype.K)(value, index) ?
trues : falses).push(value);
});
return [trues, falses];
},
pluck: function(property)
{
var results = [];
this.each(function(value, index)
{
results.push(value[property]);
});
return results;
},
reject: function(iterator)
{
var results = [];
this.each(function(value, index)
{
if (!iterator(value, index))
results.push(value);
});
return results;
},
sortBy: function(iterator)
{
return this.collect(function(value, index)
{
return {value: value, criteria: iterator(value, index)};
}).sort(function(left, right)
{
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}).pluck('value');
},
toArray: function()
{
return this.collect(Prototype.K);
},
zip: function()
{
var iterator = Prototype.K, args = $A(arguments);
if (typeof args.last() == 'function')
iterator = args.pop();
var collections = [this].concat(args).map($A);
return this.map(function(value, index)
{
return iterator(collections.pluck(index));
});
},
inspect: function()
{
return '#<Enumerable:' + this.toArray().inspect() + '>';
}
}
Object.extend(Enumerable, {
map: Enumerable.collect,
find: Enumerable.detect,
select: Enumerable.findAll,
member: Enumerable.include,
entries: Enumerable.toArray
});
var $A = Array.from = function(iterable)
{
if (!iterable) return [];
if (iterable.toArray)
{
return iterable.toArray();
}
else
{
var results = [];
for (var i = 0; i < iterable.length; i++)
results.push(iterable[i]);
return results;
}
}
Object.extend(Array.prototype, Enumerable);
if (!Array.prototype._reverse)
Array.prototype._reverse = Array.prototype.reverse;
Object.extend(Array.prototype, {
_each: function(iterator)
{
for (var i = 0; i < this.length; i++)
iterator(this[i]);
},
clear: function()
{
this.length = 0;
return this;
},
first: function()
{
return this[0];
},
last: function()
{
return this[this.length - 1];
},
compact: function()
{
return this.select(function(value)
{
return value != undefined || value != null;
});
},
flatten: function()
{
return this.inject([], function(array, value)
{
return array.concat(value && value.constructor == Array ?
value.flatten() : [value]);
});
},
without: function()
{
var values = $A(arguments);
return this.select(function(value)
{
return !values.include(value);
});
},
indexOf: function(object)
{
for (var i = 0; i < this.length; i++)
if (this[i] == object) return i;
return -1;
},
reverse: function(inline)
{
return (inline !== false ? this : this.toArray())._reverse();
},
inspect: function()
{
return '[' + this.map(Object.inspect).join(', ') + ']';
}
});
var Hash = {
_each: function(iterator)
{
for (var key in this)
{
var value = this[key];
if (typeof value == 'function') continue;
var pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
},
keys: function()
{
return this.pluck('key');
},
values: function()
{
return this.pluck('value');
},
merge: function(hash)
{
return $H(hash).inject($H(this), function(mergedHash, pair)
{
mergedHash[pair.key] = pair.value;
return mergedHash;
});
},
toQueryString: function()
{
return this.map(function(pair)
{
return pair.map(encodeURIComponent).join('=');
}).join('&');
},
inspect: function()
{
return '#<Hash:{' + this.map(function(pair)
{
return pair.map(Object.inspect).join(': ');
}).join(', ') + '}>';
}
}
function $H(object)
{
var hash = Object.extend({}, object || {});
Object.extend(hash, Enumerable);
Object.extend(hash, Hash);
return hash;
}
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
initialize: function(start, end, exclusive)
{
this.start = start;
this.end = end;
this.exclusive = exclusive;
},
_each: function(iterator)
{
var value = this.start;
do {
iterator(value);
value = value.succ();
} while (this.include(value));
},
include: function(value)
{
if (value < this.start)
return false;
if (this.exclusive)
return value < this.end;
return value <= this.end;
}
});
var $R = function(start, end, exclusive)
{
return new ObjectRange(start, end, exclusive);
}
/*
* 定义 Ajax 对象, 静态方法 getTransport 方法返回一个 XMLHttp 对象
*/
var Ajax = {
getTransport: function()
{
return Try.these(
function()
{
return new XMLHttpRequest()
},
function()
{
return new ActiveXObject('Msxml2.XMLHTTP')
},
function()
{
return new ActiveXObject('Microsoft.XMLHTTP')
}
) || false;
},
activeRequestCount: 0
}
Ajax.Responders = {
responders: [],
_each: function(iterator)
{
this.responders._each(iterator);
},
register: function(responderToAdd)
{
if (!this.include(responderToAdd))
this.responders.push(responderToAdd);
},
unregister: function(responderToRemove)
{
this.responders = this.responders.without(responderToRemove);
},
dispatch: function(callback, request, transport, json)
{
this.each(function(responder)
{
if (responder[callback] && typeof responder[callback] == 'function')
{
try
{
responder[callback].apply(responder, [request, transport, json]);
}
catch (e)
{
}
}
});
}
};
Object.extend(Ajax.Responders, Enumerable);
Ajax.Responders.register({
onCreate: function()
{
Ajax.activeRequestCount++;
},
onComplete: function()
{
Ajax.activeRequestCount--;
}
});
/*
* 我以为此时的Ajax对象起到命名空间的作用。
* Ajax.Base 声明为一个基础对象类型
* 注意 Ajax.Base 并没有使用 Class.create() 的方式来创建,我想是因为作者并不希望 Ajax.Base 被库使用者实例化。
* 作者在其他对象类型的声明中,将会继承于它。
* 就好像 java 中的私有抽象类
*/
Ajax.Base = function()
{
};
Ajax.Base.prototype = {
setOptions: function(options)
{
this.options = {
method: 'post',
asynchronous: true,
contentType: 'application/x-www-form-urlencoded',
parameters: ''
}
Object.extend(this.options, options || {});
},
responseIsSuccess: function()
{
return this.transport.status == undefined
|| this.transport.status == 0
|| (this.transport.status >= 200 && this.transport.status < 300);
},
responseIsFailure: function()
{
return !this.responseIsSuccess();
}
}
/*
* Ajax.Request 封装 XmlHttp
*/
Ajax.Request = Class.create();
/*
* 定义四种事件(状态), 参考http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp
*/
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
initialize: function(url, options)
{
this.transport = Ajax.getTransport();
this.setOptions(options);
this.request(url);
},
request: function(url)
{
var parameters = this.options.parameters || '';
if (parameters.length > 0) parameters += '&_=';
try
{
this.url = url;
if (this.options.method == 'get' && parameters.length > 0)
this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
Ajax.Responders.dispatch('onCreate', this, this.transport);
/*
* 此处好像强制使用了异步方式,而不是依照 this.options.asynchronous 的值
*/
this.transport.open(this.options.method, this.url,
this.options.asynchronous);
/*
* 这里提供了 XmlHttp 传输过程中每个步骤的回调函数
*/
if (this.options.asynchronous)
{
this.transport.onreadystatechange = this.onStateChange.bind(this);
setTimeout((function()
{
this.respondToReadyState(1)
}).bind(this), 10);
}
this.setRequestHeaders();
var body = this.options.postBody ? this.options.postBody : parameters;
this.transport.send(this.options.method == 'post' ? body : null);
}
catch (e)
{
this.dispatchException(e);
}
},
setRequestHeaders: function()
{
var requestHeaders =
['X-Requested-With', 'XMLHttpRequest',
'X-Prototype-Version', Prototype.Version,
'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];
if (this.options.method == 'post')
{
requestHeaders.push('Content-type', this.options.contentType);
if (this.transport.overrideMimeType)
requestHeaders.push('Connection', 'close');
}
if (this.options.requestHeaders)
requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
for (var i = 0; i < requestHeaders.length; i += 2)
this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i + 1]);
},
onStateChange: function()
{
var readyState = this.transport.readyState;
/*
* 如果不是 Loading 状态,就调用回调函数
*/
if (readyState != 1)
this.respondToReadyState(this.transport.readyState);
},
header: function(name)
{
try
{
return this.transport.getResponseHeader(name);
}
catch (e)
{
}
},
evalJSON: function()
{
try
{
return eval('(' + this.header('X-JSON') + ')');
}
catch (e)
{
}
},
evalResponse: function()
{
try
{
return eval(this.transport.responseText);
}
catch (e)
{
this.dispatchException(e);
}
},
/*
* 回调函数定义在 this.options 属性中,比如:
var option = {
onLoaded : function(req) {...};
......
}
new Ajax.Request(url, option);
*/
respondToReadyState: function(readyState)
{
var event = Ajax.Request.Events[readyState];
var transport = this.transport, json = this.evalJSON();
if (event == 'Complete')
{
try
{
(this.options['on' + this.transport.status]
|| this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
|| Prototype.emptyFunction)(transport, json);
}
catch (e)
{
this.dispatchException(e);
}
if ((this.header('Content-type') || '').match(/^text\/javascript/i))
this.evalResponse();
}
try
{
(this.options['on' + event] || Prototype.emptyFunction)(transport, json);
Ajax.Responders.dispatch('on' + event, this, transport, json);
}
catch (e)
{
this.dispatchException(e);
}
if (event == 'Complete')
- 21:06
- 浏览 (480)
- 评论 (1)
- 分类: JavaScript
- 相关推荐
发表评论
- 浏览: 8488 次
- 性别:

- 来自: 沈阳

- 详细资料
搜索本博客
最近加入圈子
最新评论
-
IDEA 7.0正式版注册机
在cmd模式下用就可以了
-- by commond -
IDEA 7.0正式版注册机
可以用!!3q
-- by raining_cn -
IDEA 7.0正式版注册机
不好意思,粘错了。 huxiangwei/792-6EFX0-D5FDZ-P7L ...
-- by comeon -
IDEA 7.0正式版注册机
huxiangwei 5506-D8M4K-YNY13-1HXTZ-A4290- ...
-- by comeon -
IDEA 7.0正式版注册机
只有window版,没有for java版吗?
-- by 无聊神灯






评论排行榜