prop-filter
v0.0.1
Published
A lightweight, non-intrusive object property read-write filter proxy. Can be easily and freely combined with any project environment.
Downloads
3
Maintainers
Readme
prop-filter - 对象属性读写代理
一个羽量级、非侵入式的对象属性读写过滤器代理,能轻松、自由的与任何项目环境结合,瞬间提升你的编程效率,更好的提升你的代码的重用性。
写 Javascript 代码时经常伴随着大量的对象属性的读写,常常需要针对特定范围的对象实例,编写特定的 get/set 的方法。这些方法不但让一个类设计变得臃肿,往往实际情况是,费劲巴拉的写了一大堆 get/set ,真正被用到的可能只有那么一两个,一个项目写下来,总会思考,有没有可能将 set/get 方法可以作为独立模块或者 Class ,进行统一的管理和升级,但同时又不破坏原生类的业务逻辑呢?
经过很长时间的思考和总结,将属性读写(get/set)作为代理,是最佳的模式,最终呈现为 prop-filter
这个类库。
虽然基于 ES 的 Object.definedProperties
属性可以很好实现 get/set ,但他某种程度上,让你的实际代码变得非可预期性,后来者需要花更多的精力去理解、调试你的代码,或者依赖于你的文档,去解释和说明那些属性有 getter 或 setter ,这样并不能让一个项目变得更加直观。
如无意外,所有本人发布的项目统一优先发布在 码云 。
并且,再如无意外,都是基于面向对象的方式继承重载,也不会再去写什么原型链,forget it, embrace the future!
项目依赖类库
- deepmerge,lodash.merge 的行为方式已经改为和 Object.assign 一样,但
deepmerge
现存一个问题,就是如果deepmerge([], {})
,第一个参数为数组[]
时候,合并的返回的结果也会转为一个对象结构{}
,其他方面还是符合对 deepmerge 行为的需求,所以继续使用这个类库。 - lodash,这里使用的是 _.has 、 _.get 、 _.set 这三个方法,一直在别的项目使用这一组方法,非常合用,也就没必要单独再造轮子了。
- typechecker,API DOC,主要用于变量类型判定,这个库的判断结果,基本与 javascript 原生的
typeof
的结果一致。npm上用于判定类型的库很多,但很多库自己导入了一些新的设定,你需要花时间去理解和消化那些新的设定和返回结果,这样非常不好,典型自造轮子。 - php-trim-plus,自己的类库,主要用于强化字符串的处理、空字符的检查和强化JS
trim
函数。
打包说明
index.js
- 不合并依赖库,采用 commonjs 风格,作为 nodejs 环境引入的推荐。
PropFilter.js
- 作为引入 src/PropFilter.mjs 的入口,不合并依赖库,采用 mjs 风格( import / export ),用于需要使用 mjs 环境下。 lodash 方法使用 import getProp from "lodash/get"
方式引入
PropFilter.lodash-standalone.js
- 区别在于直接引入 import _ from "lodash"
,一些项目可能存在 lodash 作为外部包,没有和 webpack 项目一起打包合并。
dist/prop-filter.bundle.js
- 全部依赖库合并打包(采用 rollup.js ),使用 commonjs 风格。
dist/prop-filter.bundle.umd.js
- 全部依赖库合并打包(采用 rollup.js ),使用 umd 风格,全局变量名称 PropFilter
。
dist/prop-filter.lodash-standalone.js
- lodash 作为项目的独立包存在,只有 lodash 使用 require('lodash')
dist/prop-filter.lodash-standalone.umd.js
- lodash 作为全局对象存在(注意是 _
),没有合并打包
安装说明
npm install prop-filter
// or
yarn add prop-filter
基础使用示例
基础三连星 has
get
set
const Filter = require('prop-filter');
let firstFilter = new Filter({
key1: {
/**
* 对象属性 getter 过滤器
* @param {*} value 从取值的对象的 key 中所取得到的值
* @param {{}} data 要取值的对象
* @param {string} key 要取值的 key
* @return {*} 返回的结果,为到 filter.get 方法所返回的值。
*/
get: (value, data, key) => { // getter
return value; // 返回 get 的值
},
/**
* 对象属性 setter 过滤器
* @param {*} value 要向对象的 key 写入的值
* @param {{}} data 要写入属性的对象
* @param {string} key 要写入值的 key
* @return {*} 返回的结果,为最终要写入到对象属性上的值。
*/
set: (value, data, key) => { // setter
return value; // 返回 要写入 的值
},
/**
* 判定对象的某个 key 是否有定义值
* @param {*} value 从取值的对象的 key 中所取得到的值
* @param {boolean} has 判定该值是否存在的初步结果
* @param {{}} data 要取值的对象
* @param {string} key 要判断值是否存在的 key
* @return {boolean} 返回的结果,最终输出 filter.has 的结果。
*/
has: (value, has, data, key) => { // has
return has;
}
}
});
// 目标对象
let target = {key1: 'value1'};
let anotherTarget = {key1: 'another-value1'};
filter.attach(target); // 将 filter 附着到目标对象上
if (filter.has('key1')) { // exists ?
let key1Value = filter.get('key1'); // getter
filter.set('key1', `changed-${key1Value}`); // setter
}
filter.unattach(target); // 解除附着
// or
filter.attach(anotherTarget); // 直接附着另外一个目标
基础三连星 - 特别说明:
1、因为使用了 _.has
作为 has
的判定基础,形成基础的 has
值,而 lodash 的 _.has
,某程度上,更应该叫做 existsKey 或 containKey ,即他返回的结果,是以目标对象是否存在这个 key 为判定基础,所以,如下情况:
let obj = {key: undefined};
_.has(obj, 'key'); // 这里会返回 true ,因为是 existsKey 或 containKey 的逻辑,而不是检查 key 的值是否为 undefined
所以,如果对应到 filter 的使用上,如果要过滤掉 undefined 和 null 的话:
let filter = new Filter({
key1: {
has: (value, has, data, key) => {
if (typeof value === 'undefined' || value === null)
return false;
return has;
}
}
});
2、三连星的方法声明区别,箭头函数 vs function。箭头函数,默认是不绑定 this 上下文的,所以当你需要使用到 filter 一些自带的类型判定时,则应该清楚知道 箭头函数 和 function 的区别。如果在箭头函数希望引用回当前 filter ,可在参数声明添加一个参数。
let filter1 = new Filter({
key1: {
get: (value, data, key, filter) => {
// 这里的 this 不是当前的 filter ,而是全局对象。
if (filter.isEmptyString(value))
return "hello world!";
return value;
}
}
});
let filter2 = new Filter({
key1: {
get: function(value, data, key) {
// 这里的 this 指向了 filter。
if (this.isEmptyString(value))
return "hello world!";
return value;
}
}
});
当然,你也可以这样写:
let filter2 = new Filter((filter) => {
key1: {
get: (value, data, key) => {
// 这里的 this 指向了 filter。
if (filter.isEmptyString(value))
return "hello world!";
return value;
}
}
});
三连星的实现本体 filter
方法
实际上实现三连星的本体,是 Filter.filter ,所以如果你需要有自定义的 filter ,请尽情在你自己的子类中重载这个方法:
// 实现1:前置重载
class MyFilter1 extends Filter {
// override
filter(target, method, prop, value = undefined) {
if (method === 'get') {
// do something
return 'anything';
}
return super.filter(target, method, prop, value);
}
}
// 实现1:后置重载
class MyFilter1 extends Filter {
// override
filter(target, method, prop, value = undefined) {
try {
return super.filter(target, method, prop, value);
} catch (e) { // 未知的处理 method 默认会抛出一个异常,这里先接住这个异常
switch (method) { // 开始加入自己的一些后置方法处理
case 'hello' :
// do something
return '...';
break;
}
throw e; // 其他未知的处理方法,继续抛出异常。
}
}
}
多层级属性的读写过滤代理
是的,这也是引入 lodash 的根本原因。
let obj = {
key: {
subkey: ['a']
}
}
let filter1 = new Filter({
'key.subkey.0': { //
get: (value, data, key, filter) => {
// 这里的 this 不是当前的 filter ,而是全局对象。
if (filter.isEmptyString(value))
return "hello world!";
return value;
}
}
});
Filter.convertPropName
这个方法,是对属性名的过滤的实现,因为 lodash 的 _.get
支持两种多层级的 key 访问 key.subkey.0
or key.subkey[0]
,Filter.convertPropName
主要为了统一属性名的访问,可根据自己喜欢,重载这个方法,默认的行为将 []
替换为了 .
操作符。
基础构成方法
filter.setFilters(filters)
设置绑定更多的 filters ,filters 必须为非空对象( object )。
filter.isValidTarget(target)
判定目标是否为有效的可附着目标,当前这个目标必须是一个 object。
filter.verifyTarget()
检查当前 filter 是否附着了有效的目标,如果没有,会直接抛出异常,正常则返回目前附着的目标。
filter.attach(target)
和 filter.unattach(target)
附着目标 和 解除附着
filter.isAttached(target = undefined)
判定当前 filter 是否已经附着了有效的目标,如果指定了 target 参数,则判定 filter 附着的目标是否为 target。
其他辅助工具方法
php-trim-plus 系列,请参考 使用说明
filter.toSafeString(str); // 转换为安全字符串
filter.trim(str, charList, isPlus); // trim
filter.ltrim(str, charList, isPlus); // trimStart
filter.rtrim(str, charList, isPlus); // trimEnd
filter.isString(str); // 是否字符串
filter.isEmptyString(str); // 是否空字符
filter.isEmptyStringOrWhitespace(str); // 是否空字符,或 空格
filter.isNothing(value); // 是 undefined or null
filter.isNotNothing(value); // 非 undefined or null
filter.isUndefined(value); // 是 undefined
filter.isNotUndefined(value); // 非 undefined
filter.isArray(value); // 是 数组
filter.isEmptyArray(value); // 是 空数组
filter.isStringNumber(value); // 是 字符串数值
filter.isFunction(value); // 是 函数,目前版本使用的是 typeof value === 'function' ,
// 目前发现,typechecker.isSyncFunction 和 typechecker.isAsyncFunction 判定失效,
// 部分 js 环境无法识别出来。
typechecker 系列,请参考 API DOC
filter.getType(value); // 获取变量基础类型
filter.isObject(value); // 是 对象
filter.isPlainObject(value); // 是 纯 object
filter.isEmptyObject(value); // 是 空 object
filter.isNumber(value); // 是 数值类型
filter.isRegExp(value); // 是 正则表达式
filter.isError(value); // 是 异常错误对象