Hash是Prototype作者擴充出來的一個資料類型。本質上他就是一個普通的javascript對象(註:不要糾結什麼javascript變數都是對象,這裡說new Object()那種),然後在這個對象上面擴充出來一些其他的方法。
基本原理
基本的原理的代碼說明就是:
function Hash(object){
this._object = object;
}
Hash.prototype = {
constructor : Hash,
method_1 : function(){//this._object},
method_2 : function(){//this._object}
//...
}
var hash_1 = new Hash({name : 'xesam'});
不過在源碼中肯定不是這樣的咯,建立類的形式就不是這樣,因此我們換成Paototype中建立類的形式(註:Prototype源碼淺析——Class部分(一)之類):
var Hash = Class.create(Enumerable, (function() {
function initialize() {
//this._object
}
return {
initialize : initialize
}
})())
Hash裡面也混入了Enumerable對象,藉此給Hash增加更多的方法。
(在這裡我們有個區別可以留意一下:儘管Hash和Array都混入了Enumerable,但是與Array不同的是,Array是js原生的一個資料類型,只能用extend的方式,Hash是作者自己建立的一個資料類型,因此,Hash採用的方式是Class.create的方式。)
既然混入了Enumerable對象,那麼肯定有一個_each方法,對於Hash對象,_each方法的iterator參數的參數是一個pair,這個pair比較特別一點
既是一個數組[key,pair],也是一個對象:
{
key : key,
value : value
}
因此pair[0]和pair.key的結果是一致的。
基本操作:
看下建立類時的initialize方法,每一個hash對象內部都維護著一個內部的對象,變數名叫做_object,因此原則上不能通過直接操作執行個體的屬性來讀取或者設定執行個體的屬性。所以作者提供了單獨的方法,現在有三個基本的方法:get,set,unset。
function set(key, value) {
return this._object[key] = value;
}
function get(key) {
if (this._object[key] !== Object.prototype[key])
return this._object[key];
}
function unset(key) {
var value = this._object[key];
delete this._object[key];
return value;
}
(註:Prototype的類很基礎,封裝基本沒有,因此雖然內部維護了一個_object變數,但是並不是真正意義上的私人變數,訪問hash的某個屬性可以用上面的get,set,unset,也可以直接修改_object對象:
hash.set('name','new name')
hash._object.name = 'new name'
上面操作沒區別,靠自覺而已。)
複製hash:
第一:
複製當前hash對象(hash)——clone。複製內部的對象(_object)——toObject
toObject調用的是Object.clone方法,現在回顧一個下Object.clone方法
function clone(object) {
return extend({ }, object);
}
就是把this._object複製到一個Null 物件裡面,然後返回。
function clone() {
return new Hash(this);
}
現在看一看建立Hash類的時候的initialize函數,先前忽略了this._object的初始化過程:
function initialize(object) {
this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
}
看看實現:如果object是一個普通對象,那麼直接調用Object.clone方法。如果object是一個Hash執行個體,那麼調用object.toObject,注意object.toObject的本質。間接調用的還是Object.clone方法,不過最終返回的類型是Hash類型,享有Hash的各種方法。
取得Hash對象的keys和values數組
方法名就是keys和values。這裡使用的是父物件的一個方法——plunk(註:plunk是Enumerable中定義的一個方法Prototype源碼淺析——Enumerable部分(三)):
function keys() {
return this.pluck('key');
}
function values() {
return this.pluck('value');
}
與plunk關係最密切的一個方法是each,回去看下each,each函數的iterator的參數是pair,是一個對象。
更新(合并)一個Hash執行個體:
這樣的操作有兩種:一種是破壞性的update(會修改原始對象),一種是非破壞性的merge(不會修改原始對象的)。
update使用的是inject方法(註:Prototype源碼淺析——Enumerable部分(三)),第一個參數是this(當前Hash執行個體),因此當前對象會被修改。
merge使用的也是update方法,不過先執行了一次clone操作,因此原始的Hash執行個體被保留下來了。
function merge(object) {
return this.clone().update(object);//先拷貝了內部變數_object
}
function update(object) {
return new Hash(object).inject(this, function(result, pair) {
result.set(pair.key, pair.value);
return result;
});
}
格式轉換:
toQueryPair 和 toQueryString
toQueryPair是一個內部使用的方法,主要作用是檢測value的值。如果value沒為undefined,那麼只保留key值,如果value值為null,那麼保留“key=”形式否則轉換為key=encodeURIComponent(value)的形式。
function toQueryPair(key, value) {
if (Object.isUndefined(value)) return key;
return key + '=' + encodeURIComponent(String.interpret(value));
}
toQueryString主要涉及到一個處理values是數組的情況,values為數組就使用concat展開,values為字串就直接push,然後再展開。
這裡和Object.toQueryString方法是一樣的,應該是可以相互代替的。這裡回頭去看Object裡面的Str方法,那個清楚了,這個就不在話下了(註:Prototype源碼淺析——Object部分(三)之有關JSON)。
inspect就沒什麼好說的了。很常見了。
轉載請註明來自小西山子【http://www.cnblogs.com/xesam/】
本文地址:http://www.cnblogs.com/xesam/archive/2012/01/31/2332918.html