options
A very light-weight in-code option parsers for node.js.
一個非常輕量的在代碼內的NodeJS選項解析器。
大致的作用就是解析、重設、合併、拷貝、讀取、驗證選項,源代碼非常短,本文就做一個簡單的解讀。
這個包非常底層,沒有依賴其他的第三方包(葉子包),而且被廣泛地引用,在自行開發包的時候可以引用一下。
這個包是隻支持Node的,不適用於瀏覽器,因爲它主要操作配置文件,引用了NodeJS的 fs
包。
代碼註釋
老實說這個代碼寫得真不怎麼樣。
/*!
* Copyright(c) 2011 Einar Otto Stangvik <[email protected]>
* MIT Licensed
*/
var fs = require('fs');
/**
* Options 是一個類,其構造函數的僅有一個參數:默認選項。
* @params: defaults, 默認的參數選項
* @note: defaults在代碼中會以閉包的形式保存(老實說這不是一個好方法)
* @note: 沒有在Options中使用原型來生成方法
* @note: 成員變量設定不清楚
*/
function Options(defaults) {
var internalValues = {};
var values = this.value = {}; // 老實說通讀了代碼我覺得這個變量就是雞肋
// 將defaults深拷貝到internalValues中
Object.keys(defaults).forEach(function(key) {
internalValues[key] = defaults[key];
// 定義對象的屬性
Object.defineProperty(values, key, {
get: function() { return internalValues[key]; }, // 偷樑換柱地重載了getter
configurable: false, // 不可配置
enumerable: true // 可枚舉
});
});
/**
* reset: 重置選項
* @return: Options對象自身
* */
this.reset = function() {
// 將defaults深拷貝到internalValues中
Object.keys(defaults).forEach(function(key) {
internalValues[key] = defaults[key];
});
return this;
};
/**
* Merge: 合併選項
* @params: options, 需要合併的選項
* @params: required, options 中要包含的鍵的數組
* @throw: 當 required 中某值key使得 options[key] == undefined 時
* @return: Options對象自身
* */
this.merge = function(options, required) {
// 初始化新的options
options = options || {};
// 若 required 是數組
if (Object.prototype.toString.call(required) === '[object Array]') {
// 遍歷 required, 檢查options與required之間是否missing。
var missing = []; // 注意此處的變量提升
for (var i = 0, l = required.length; i < l; ++i) {
var key = required[i];
if (!(key in options)) {
missing.push(key);
}
}
// missing 報錯
if (missing.length > 0) {
if (missing.length > 1) {
throw new Error('options ' +
missing.slice(0, missing.length - 1).join(', ') + ' and ' +
missing[missing.length - 1] + ' must be defined');
}
else throw new Error('option ' + missing[0] + ' must be defined');
}
}
// 將options中的值深拷貝入internalValues
Object.keys(options).forEach(function(key) {
if (key in internalValues) {
internalValues[key] = options[key];
}
});
return this;
};
/**
* copy: 拷貝選項
* @params: keys, 需要拷貝的鍵的數組。
* @return: 新的對象
* */
this.copy = function(keys) {
var obj = {};
// 將defaults中所有在keys中的鍵找出來複製到obj中。
// 吐槽: 照理說不是應該遍歷keys比較正常嗎
Object.keys(defaults).forEach(function(key) {
if (keys.indexOf(key) !== -1) {
obj[key] = values[key];
}
});
return obj;
};
/**
* read: 從文件中以JSON格式讀取選項
* @params: filename, 文件名
* @params: cb, 回調函數
* @note: 當cb未定義時使用同步讀取方式,否則使用異步。
* @return: Options對象自身
* */
this.read = function(filename, cb) {
// 判斷cb的類型
if (typeof cb == 'function') {
// 異步讀取
var self = this;
fs.readFile(filename, function(error, data) {
if (error) return cb(error); // 將error回傳給回調
var conf = JSON.parse(data); // JSON格式解析
self.merge(conf); // 將options合併
cb(); // 吐槽: 作者的設計是就是不給回調任何關於data的信息
});
}
else {
// 同步讀取
var conf = JSON.parse(fs.readFileSync(filename));
this.merge(conf); // 吐槽: 你倒是嵌套地徹底一些啊
}
return this;
};
/**
* isDefined: 謂詞:某個參數是否被定義
* @params: key, 某個參數
* @return: boolean值, 此參數是否被定義
* */
this.isDefined = function(key) {
return typeof values[key] != 'undefined';
};
/**
* isDefinedAndNonNull: 謂詞:判斷某個參數是否被定義且非空
* @params: key, 某個參數
* @return: boolean值, 此參數是否被定義且非空
* */
this.isDefinedAndNonNull = function(key) {
return typeof values[key] != 'undefined' && values[key] !== null;
};
// 凍結對象
Object.freeze(values); // 凍結默認值
Object.freeze(this); // 凍結
}
// 導出Options類
module.exports = Options;
讀後感大概就是MDZZ。