condition-operat

condition-operat 可以对一系列复杂的条件进行逻辑运算,条件可以是基本类型的数据,也可以是个函数,甚至可以是个异步条件,即 Promise;或者是个条件集,条件集也可以再嵌套条件集;可以指定条件之间的逻辑关系,如:与、或、非

Usage no npm install needed!

<script type="module">
  import conditionOperat from 'https://cdn.skypack.dev/condition-operat';
</script>

README

1. 简介

conditionOperat 可以对一系列复杂的条件进行逻辑运算,条件可以是基本类型的数据,也可以是个函数,甚至可以是个异步条件,即 Promise;或者是个条件集,条件集也可以再嵌套条件集;可以指定条件之间的逻辑关系,如:与、或、非;

如果您在使用的过程中遇到了问题,或者有好的建议和想法,您都可以通过以下方式联系我,期待与您的交流:

  • 给该仓库提交 issues
  • 给我 Pull requests
  • 邮箱:guobinyong@qq.com
  • QQ:guobinyong@qq.com
  • 微信:keyanzhe

2. 特性

  • 可以指定条件条件表达式的间的逻辑关系:与、或、非;

  • 条件表达式可以任意层级嵌套,即:条件集 可以 嵌套 条件集;

  • 短路运算 在对条件表达式进行运算的过程中,如果运算中途已经能够确认最终的运算结果,则便会停止对剩余表达式的计算,并返回计算结果;

  • 简单优先 为了提高运算效率,除了加入了短路运算的特性外,还加入了简单优先的计算原则,即:对于同一层级表达式,会按照下面的顺序优先计算:

    1. BaseCondition | FunCondition: 除了 异步条件 PromCondition、条件集 ConditionSet 以外的所有其它数据类型的条件表达式,这些条件会被当作布尔值来计算;
    2. ConditionSet : 条件集;
    3. PromCondition : 异步条件;

3. 安装方式

目前,安装方式有以下几种:

方式1:通过 npm 安装

npm install --save condition-operat

方式2:直接下载原代码

您可直接从 git仓库 下载代码文件; 仓库里包含了 源码 和 编译后的代码文件 :

  • conditionOperat 的源码文件是 src/conditionOperat 有 TypeScript 版本 和 JavaScript 版本;
  • conditionOperat 的编译后的文件是 dist/conditionOperat.js

您可以直接把任一 源码 或 编译后 的文件拷贝到您的项目中去;然后使用如下代码在您的项目中引入 conditionOperat

import { conditionOperat , create } from "path/to/package/conditionOperat";

或者

import conditionOperat from "path/to/package/conditionOperat";

方式3:通过<script>标签引入

您可直接下载 git仓库 中的 dist/conditionOperat.js 文件,然后使用如下代码引用 和 使用 conditionOperat:

引用 conditionOperat.js

<script src="path/to/package/conditionOperat.js"></script>

使用全局的 conditionOperat() 或 快捷工具 conditionOperat.create()

<script>
// 使用全局的 conditionOperat()
    var condExpr = [true,false,true];
    conditionOperat(condExpr);
// 使用快捷工具
     var operatWith = conditionOperat.create(condExpr);
</script>

4. 教程

如果需要了解详细的接口信息,请到 API接口文档

4.1. 最简单的使用

// 求表达式 true 的值
conditionOperat(true);  //结果: true

4.2. 与运算

对于基本类型的数据(如:boolean、number、string、symbol、undefined、null )都会被作为布尔值来对待,即会被简单的转为布尔类型;

// 对一组表达式做 与 运算;
var condExpr = [
  true,
  false,
  "字符串会作为布尔值对待",
  34,
  0, // 相当于 false
];

conditionOperat(condExpr);  //结果: false

或者

// 对一组表达式做 与 运算
var condExpr = [
  true,
  false,
  true
];

condExpr.rel = "and";   // rel 可设置为  "and" (与运算) 或  "or"(或运算), 如果没设置 rel ,则默认会用 与运算 "and"

conditionOperat(condExpr);  //结果: false

4.3. 或运算

// 对一组表达式做 或 运算
var condExpr = [
  true,
  false,
  true
];

condExpr.rel = "or"; //设置 数组中元素之间关系为 或 ; 即:对所有元素进行 或运算

conditionOperat(condExpr);  //结果: true

4.4. 先或后非

var condExpr = [
  true,
  false,
  true
];

condExpr.rel = "or"; //设置:数组中元素之间关系为 或 ; 即:对所有元素进行 或运算
condExpr.not = true; //设置:对运算结果 取反,即 对所有元素进行 或运算之后 再取反

conditionOperat(condExpr);  //结果: false

4.5. 表达式可以嵌套

//里面的表达式
var innerExpr = [
  false,
  true
];

innerExpr.rel = "or";


var condExpr = [
  true,
  innerExpr,   // 表达式可以嵌套表达式
  true
];
condExpr.not = true;


conditionOperat(condExpr);  //结果: false

4.6. 函数类型的表达式

函数也可作为表达式,运算时会将函数的返回值作新的表达式重新计算,如果函数返回的还是函数,则会对该返回的函数继续运算; 函数也可以返回一个 Promise,针对 Promise类型表达式的运算方式,详见[Promise类型的表达式][]

var condExpr = [
  function(){return false},   //表达式可以是个返回 另一个表达式的函数
  function(){return "返回其它类型的值"},
  function(){
    return ()=>{return 5}
  },  //函数表达式 可以返回 另一个函数表达式
  true,  //表达式的类型可以混合使用
];

conditionOperat(condExpr);  //结果: false

在执行条件运算 conditionOperat() 时,你也可以设置函数条件在被调用时的 this 值 和 参数,如下:

// 验证名字
function verifyName(phoneNum,gender){
  var name = this.value;
  return name.trim().length > 0
}

//验证手机号
function verifyPhone(phoneNum,gender){
  return /\d{11}/.test(phoneNum)
}


// 验证性别
function verifyGender(phoneNum,gender){
  return /^男|女$/.test(gender)
}


// 条件表达式:名字、手机号、性格慎必须都要符合要求
var condExpr = [verifyName,verifyPhone,verifyGender];


// 获取保存名字的输入框的dom元素 来作为 函数条件的 this 的值;
var thisValue = document.getElementById("nameInput");

// 给函数条件传递两个参数:手机号  和 性别
var args = ["17639609033","男"];

// conditionOperat() 的第一个参数是 条件表达式,第二个参数是 函数条件的 this 值,第三个参数是 函数条件 的参数数组
conditionOperat(condExpr,thisValue,args);

注意:
thisValue 和 args 会被应用到所有的 函数条件,包括那些 运算过程 中产生的函数条件,比如:函数条件返回的函数条件、异步条件决议时传递出的 函数条件

4.7. 异步表达式

Promise 也可以作为表达式,当 Promise 作为表达式时,会根据 resolve 的值来进行计算,如果 Promise 是被 reject 了,则会被作为 假 false 来处理;

Promise 决议的值也可以是其它复杂的条件表达式,如:函数、数组(条件集)等等;

var condExpr = [
  new Promise(function (resolve, reject) {
    setTimeout( ()=>{
      resolve(false)
    },1000)
  }),
  new Promise(function (resolve, reject) {
    setTimeout( resolve,2000,false)
  }),
  new Promise(function (resolve, reject) {
    setTimeout( ()=>{
      reject("reject的参数")   //被 reject 的 Promise 会被作为 假值 来对待
    },3000)
  }),
];

condExpr.rel = "or";  //设置各 Promise 之间是 或 的关系

conditionOperat(condExpr).then(function (res) {
console.log(res)
});  //3秒后输出: false

4.8. 非运算

对于任何对象(比如:普通对象、函数、Promise、数组 等等),都可以通过向其添加 not 属性来设置 非运算;设置非运算后,会先对 该对象进行求值,然后再对求得的值取反;

4.8.1. 普通对象的非运算

/*
 先对对象 求值,然后再取反;
 由于对象 转成布尔后 为 true,对 true 取反后 为 false ,所以 对 条件表达式 {not:true} 求值后 得 false
 */
var condExpr = {not:true};
conditionOperat(condExpr);  //结果: false

4.8.2. 函数的非运算

/*
 先对函数 求值,然后再取反;
 先对函数 求值,得 true,再取反,得 false
 */
var condExpr = function(){
  return true
};
condExpr.not = true;
conditionOperat(condExpr);  //结果: false

4.8.3. 异步非运算

/*
 先对 Promise 求值,然后再取反;
 选对 Promise 求值,得到异步的值 true,再取反后,得 false
 */
var condExpr = new Promise(function(resolve,reject){
  setTimeout(()=>{
    resolve(true)
  },1000)
});
condExpr.not = true;

conditionOperat(condExpr).then(function(vlaue){
  console.log(value);  //结果: false
});

4.8.4. 条件集的非运算

/*
 先运算 数组中所有元素相或 的值,为 true,然后再取反,得 false
 */
var condExpr = [
  true,
  0,
  {}
];
condExpr.rel = "or";    //设置 或 关系

condExpr.not = true;

conditionOperat(condExpr);  //结果: false

4.9. 异步表达式与其它类型的表达式混合使用

Promise类型的条件表达工也可以与其它类型的条件表达式混合使用;

对于带有 Promise 类型的条件表达式,conditionOperat(condExpr) 返回的结果可能是 布尔值,也可能是 Promise ;这取决于通过 非 Promise 的表达式是否能求出 condExpr 的值,如果能,则返回的是布尔值,如果不能,则 condExpr 的值只能依赖于 其中的 Promise 表达式,则 conditionOperat(condExpr) 就会返回一个 Promise ;

如果 返回的是 Promise ,侧 Promise 决义后的值值便是表达式 condExpr 的运算结果;

var condExpr = [
  true,  //布尔值作为条件,直接表示条件成立或失败
  false,
  3.5,  //数字会被转为布尔类型;
  0,   //0 相当于 false
  "字符串也可作为表达式",  //字符串也会被转为布尔类型;
  {},  //对象会被转为布尔类型;
  //对于任何对象(比如:函数、Promise、数组 等等),都可以通过向其添加 not 属性来设置 非运算;
  {not:true},    //对象会被转为 true,由于本身的 not 属性为 true,所以,还会对该对象本盘的值 true 再进行一次 非运算,所以该表达式的值为 false
  function () {return false},    //函数作为条件,会根据函数的返回值作为该条件的计算结果
  function () {return [false,45]},    //函数也可以返回 条件集
  [
    "",  // 空字符串相当于 false
    {mes:"对象也可作为表达式"},  //对象也可以作为条件,它会被当作真值来对待,即相当于 true
    new Promise(function (resolve, reject) {
      setTimeout( ()=> {
        resolve(true)
      },500)
    }),  // Promise 作为表达式,当被计算时,会根据 Promise 被 resolve 时的 value 来作为 计算结果;
    new Promise(function (resolve, reject) {
      var funCond = ()=>{return false};
      setTimeout( ()=> {
        resolve(funCond)
      },500)
    })     // Promise 作为表达式,当被计算时,会根据 Promise 被 resolve 时的 value 来作为 计算结果,如果 value 仍是复杂的表达式(比如:函数),还会继续计算;
  ], // 条件集,也就是数组,也可以作为条件表达式,会根据 该条件集的计算结果作为 该条件集 的结果
  new Promise(function (resolve, reject) {
    setTimeout( ()=> {
      reject("reject会被认为返回了假值")
    },300)
  })   // 如果 Promise 被 reject 了,则 该 Promise 表达式的计算结果为 false
];

condExpr.rel = "or";   //设置条件集中所有条件表达式的关系为 或
condExpr.not = true;  //对 条件集中所有表达式 进行 或运算(由rel属性指定) 之后,再对其结果取反,即:再进行 非运算;


/*
调用 conditionOperat 函数对 条件表达式 condExpr 进行求值;
返回的结果可能是 布尔值,也可能是 Promise ;这取决于通过 非 Promise 的表达式是否能求出 condExpr 的值,如果能,则返回的是布尔值,如果不能,则 condExpr 的值只能依赖于 其中的 Promise 表达式,则 conditionOperat(condExpr) 就会返回一个 Promise ;
如果 返回的是 Promise ,侧 Promise 决义后的值值便是表达式 condExpr 的运算结果
 */
var res = conditionOperat(condExpr)

if (res instanceof Promise){
  res.then((value)=>{
    console.log("condExpr条件表达式的值是:",value);
  })
}else {
  console.log("condExpr条件表达式的值是:",res);
}

4.10. 快捷工具

条件运算函数 conditionOperat(condExpress:CondExpression,thisValue?:ThisValue, args?:Args):OperatedResult 可接收如下三个参数

  • condExpress : CondExpression 条件表达式
  • thisValue ?: any 设置条件表达式中 函数条件 的 this 的值
  • args ?: any[] 设置条件表达式中 函数条件 的 参数序列

这三个参数中,只有 条件表达式 condExpress 是必须参数;

有些时候,我们可能经常需要 对同一条件表达式 condExpress 进行运算,只是传不同的 thisValue 或 args ;

比如:对表单中若干输入框进行验证,这些输入框的验证条件是一定的,但每次提交表单时,各个输入框的值是不一样的,对于这样的场景,我们每次进行条件运算时,都要传入同一 条件表达式 condExpress 和 包含各个输入框值的 args conditionOperat(condExpress,null, args) ,如果 每次表单的dom的结构都是一样的,也可以将 表单的 dom 对象 作为 thisValue 参数,让 函数条件 自动获取对应的输入框的值并验证,这样,我们不用每次再分别取各个输入框的值了,只需要给 conditionOperat() 传 条件表达式 condExpress 和 thisValue 就行了,如 conditionOperat(condExpress,thisValue)

尽管这样,每次条件运算,还是需要传入一样的 条件表达式,这是重复的操作;

为了解决这类问题,我封装了一个工具函数 create() ,它根据给定的参数,来创建专门用来接收剩余参数的条件运算函数;

有两种方式可以访问到 create() 函数:

  1. 直接导入create函数 import { create,conditionOperat } from "condition-operat";
  2. 通过 conditionOperat 的 create 方法来访问:conditionOperat.create(options);

create()的使用示例如下:

// 验证名字
function verifyName(target){
  return target.name.trim().length > 0
}


//验证手机号
function verifyPhone(target){
  return /\d{11}/.test(target.phoneNum)
}


// 验证性别
function verifyGender(target){
  return /^男|女$/.test(target.gender)
}


// 条件表达式:名字、手机号、性格慎必须都要符合要求
var condExpr = [verifyName,verifyPhone,verifyGender];



/**
 * 创建条件表达式 condExpr 和 thisValue (值为 `null`) 的快捷函数 operatWith ; 
 * 
 * 因为创建 `operatWith()` 时 给 `create()` 传了两个选项 expr 和 "this" ,还剩余一个选项 args 参数没有传,
 * 所以 这个 operatWith() 快捷函数只接收一个参数,即 args ; 
 */
var operatWith = create({
  expr:condExpr,
  "this":null
});


// 被测试的目标
var target = {
  name:"郭斌勇",
  gender:"男",
  phoneNum:""
};


// 传入 args
operatWith([target]);     //结果:false

或者

// 验证名字
function verifyName(){
  var inputDom = this.elements.name;
  var name = inputDom.value;
  return name.trim().length > 0
}


//验证手机号
function verifyPhone(){
  var inputDom = this.elements.phoneNum;
  var phoneNum = inputDom.value;
  return /\d{11}/.test(phoneNum)
}


// 验证性别
function verifyGender(){
  var inputDom = this.elements.gender;
  var gender = inputDom.value;
  return /^男|女$/.test(gender)
}


// 条件表达式:名字、手机号、性格慎必须都要符合要求
var condExpr = [verifyName,verifyPhone,verifyGender];



/**
 * 创建条件表达式 condExpr 的快捷函数 operatWith ; 
 * 
 * 因为创建 `operatWith()` 时 给 `create()` 传了一个选项 expr ,还剩余二个选项 "this" 和 args 选项没有传,
 * 所以 这个 operatWith() 快捷函数可以接收二个参数,即 "this" 和 args ; 但本示例中的 函数条件 只用到了 "this" 选项 ,没有用到 args ,所以,在使用 operatWith() 时,只需要给其传一个参数 thisValue 即可;
 */
var operatWith = create({expr:condExpr});


// 被测试的目标
var thisValue = document.getElementById("form");

// 传入 thisValue 参数
operatWith(thisValue);

其中,当传给 create() 的选项只包含 表达式 condExpr 时,可以直接将表达式 condExpr 作为参数传给 create() ,如 create(condExpr)


有您的支持,我会在开源的道路上,越走越远

赞赏码