options
A very light-weight in-code option parsers for node.js.
一个非常轻量的在代码内的NodeJS选项解析器。
大致的作用就是解析、重设、合并、拷贝、读取、验证选项,源代码非常短,本文就做一个简单的解读。
这个包非常底层,没有依赖其他的第三方包(叶子包),而且被广泛地引用,在自行开发包的时候可以引用一下。
这个包是只支持Node的,不适用于浏览器,因为它主要操作配置文件,引用了NodeJS的 fs
包。
代码注释
老实说这个代码写得真不怎么样。
/*!
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
* 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。