建立核心公用函式



在繼續進行之前,針對之後可能會使用到的一些功能,在這邊先將之封裝為公用函式,之後就可直接呼叫使用。為了管理名稱空間,這邊使用以下的骨架作為開始:
(function(global) {
    var XD = {
    };
    global.XD = XD;
})(this);

所有的公用函式將以XD作為名稱空間,XD目前只是一個純JavaScript物件,之後會再依需要修改。在這個物件上,會有各種公用函式,首先是JavaScript字串本身所缺少的裁剪前後空白的函式XD.trim()函式:
(function(global) {
    var XD = {
        trim: function(text) {
            return (text || '').replace( /^(\s|\u00A0)+|(\s|\u00A0)+\$/g, '');
        },
        ...
    };
    global.XD = XD;
})(this);

接著對於經常判斷的陣列與函式,新增了XD.isArray()與XD.isFunction()來判斷:
(function(global) {
    var XD = {
        ...
        isArray: function(obj) {
            return Object.prototype.toString.call(obj) === '[object Array]';
        },
        isFunction: function(obj) {
            return Object.prototype.toString.call(obj) === '[object Function]';
        },
       ...
    };
    global.XD = XD;
})(this);

在這邊使用的是ECMAScript規格要求Object預設toString()傳回的字串作為判斷。接著是XD.each()函式,雖然Rhino之類的直譯器陣列本身可使用forEach()方法,但在Internet Explorer中無法使用,而且這邊設計的XD.each()要更為通用,不僅可列舉陣列或類似陣列的物件上所有索引與值,還可以列舉一般物件的特性與值。
(function(global) {
    var XD = {
        ...
        each: function(obj, callback) {
            var length = obj.length,
                isObj = (length === undefined) || this.isFunction(obj);
            if (isObj) {
                for(var name in obj) {
                    if(callback.call(obj[name], obj[name], name) === false ) {
                        break;
                    }
                }
            }
            else {
                for(var i = 0, value = obj[0];
                    i < length && callback.call(obj[i], value, i) !== false;
                    value = obj[++i] ) {}
            }
            return obj;
        },

       ...
    };
    global.XD = XD;
})(this);

XD.each ()要傳入想迭代的物件,如果物件上沒有length特性,或者是本身是個函式,此時使用for in迭代,否則使用for搭配索引迭代。回呼函式的第一個參數會傳入特性值,第二個參數數會傳入特性名稱,如果想要中止迭代,可從回呼函式中傳回 false。

在JavaScript中有許多非Array的實例,但具有length與數字特性,對這種類似陣列的物件,有時將之轉換為Array實例操作會比較方便。因此在公用函式中新增了XD.makeArray()函式:
(function(global) {
    var XD = {
        ...
        makeArray: function(arrayLike) {
            if(arrayLike.length != null) {
                return Array.prototype.slice.call(arrayLike, 0)
                        .filter(function(ele) { return ele !== undefined; });
            }
            return [];
        }

    };
    global.XD = XD;
})(this);

Array.prototype.slice()方法本身是依是否有length及數字特性來進行切割,為了考量有length特性但無索引特性,對切割後的陣列中元素若為undefined的情況進行過濾。

目前完成的檔案暫時命名為gossip-0.1.js。如下所示:
(function(global) {
var XD = {
trim: function(text) {
return (text || '').replace( /^(\s|\u00A0)+|(\s|\u00A0)+\$/g, '');
},
isArray: function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
},
isFunction: function(obj) {
return Object.prototype.toString.call(obj) === '[object Function]';
},
each: function(obj, callback) {
var length = obj.length,
isObj = (length === undefined) || this.isFunction(obj);
if (isObj) {
for(var name in obj) {
if(callback.call(obj[name], obj[name], name) === false ) {
break;
}
}
}
else {
for(var i = 0, value = obj[0];
i < length && callback.call(obj[i], value, i) !== false;
value = obj[++i] ) {}
}
return obj;
},
makeArray: function(arrayLike) {
if(arrayLike.length != null) {
return Array.prototype.slice.call(arrayLike, 0)
.filter(function(ele) { return ele != undefined; });
}
return [];
}
};
global.XD = XD;
})(this);

如果將來,你想新增公用函式,也可以進一步在XD上新增,例如,因為某個理由,你經常會要建立類似陣列的物件,也許你會考慮新增一個arrayLike()函式,在你的.js檔案中可以如下撰寫:
(function(XD) {
    XD.arrayLike = function() {
        var obj = { length : arguments.length };
        XD.each(arguments, function(ele, i) {
            obj[i] = ele;
        });
        return obj;
    };
})(this.XD);

在引入gossip-0.1.js與你的.js之後,XD.arrayLike()可以這麼使用:
var obj = XD.arrayLike(1, 2, 3, 4);
XD.each(obj, function(element) {
    print(element);
});

以上在Rhino Shell中會顯示1到4的結果。