JavaScript中不得不说的断言?

断言主要应用于“调试”与“测试”

一、前端中的断言

仔细地查找一下JavaScript中的API,实际上并没有多少关于断言的方法。唯一一个就是console.assert:

  // console.assert(condition, message)const a = '1'console.assert(typeof a === 'number', 'a should be Number')

当condition为false时,该方法则会将错误消息写入控制台。如果为true,则无任何反应。

实际上,很少使用console.assert方法,如果你阅读过vue或者vuex等开源项目,会发现他们都定制了断言方法:

  // Vuex源码中的工具函数function assert (condition, msg) {if (!condition) {throw new Error(`[Vuex] ${msg}`)}}

二、Node中的断言

Node中内置断言库(assert),这里我们可以看一个简单的例子:

  try {assert(false, '这个值应该是true')} catch(e) {console.log(e instanceof assert.AssertionError) // trueconst { actual, expected, operator } = econsole.log(`实际值: ${actual},期望值: ${expected}, 使用的运算符:${operator}`)// 实际值: false,期望值: true, 使用的运算符:==}

assert模块提供了不少的方法,例如strictEqual、deepStrictEqual、notDeepStrictEqual等,仔细观察这几个方法,我们又得来回顾一下JavaScript中的相等比较算法:

  • 抽象相等比较算法 (==)
  • 严格相等比较算法 (===)
  • SameValue (Object.is())
  • SameValueZero

几个方法的区别可以查看这可能是你学习ES7遗漏的知识点。

在Node10.2.0文档中你会发现像assert.equal、assert.deepEqual这样的api已经被废除,也正是避免==的复杂性带来的易错性。而保留下来的api基本上多是采用后几种算法,例如:

  • strictEqual使用了严格比较算法
  • deepStrictEqual在比较原始值时采用SameValue算法

三、chai.js

从上面的例子可以发现,JavaScript中内置的断言方法并不是特别的全面,所以这里我们可以选择一些三方库来满足我们的需求。

这里我们可以选择chai.js,它支持两种风格的断言(TDD和BDD):

  const chai = require('chai')const assert = chai.assertconst should = chai.should()const expect = chai.expectconst foo = 'foo'// TDD风格 assertassert.typeOf(foo, 'string')// BDD风格 shouldfoo.should.be.a('string')// BDD风格 expectexpect(foo).to.be.a('string')

大部分人多会选择expect断言库,的确用起来感觉不错。具体可以查看官方文档,毕竟确认过眼神,才能选择适合的库。

四、expect.js源码分析

expect.js不仅提供了丰富的调用方法,更重要的就是它提供了类似自然语言的链式调用。

链式调用

谈到链式调用,我们一般会采用在需要链式调用的函数中返回this的方法实现:

  class Person {constructor (name, age) {this.name = namethis.age = age}updateName (val) {this.name = valreturn this}updateAge (val) {this.age = valreturn this}sayHi () {console.log(`my name is ${this.name}, ${this.age} years old`)}}const p = new Person({ name: 'xiaoyun', age: 10 })p.updateAge(12).updateName('xiao ming').sayHi()

然而在expect.js中并不仅仅采用这样的方式实现链式调用,首先我们要知道expect实际上是Assertion的实例:

  function expect (obj) {return new Assertion(obj)}

接下来看核心的Assertion构造函数:

  function Assertion (obj, flag, parent) {this.obj = obj;this.flags = {};// 通过flags记录链式调用用到的那些标记符,// 主要用于一些限定条件的判断,比如not,最终返回结果时会通过查询flags中的not是否为true,来决定最终返回结果if (undefined != parent) {this.flags[flag] = true;for (var i in parent.flags) {if (parent.flags.hasOwnProperty(i)) {this.flags[i] = true;}}}// 递归注册Assertion实例,所以expect是一个嵌套对象var $flags = flag ? flags[flag] : keys(flags), self = this;if ($flags) {for (var i = 0, l = $flags.length; i < l; i  ) {// 避免进入死循环if (this.flags[$flags[i]]) {continue}var name = $flags[i], assertion = new Assertion(this.obj, name, this)// 这里要明白修饰符中有一部分也是Assertion原型上的方法,例如 an, be。if ('function' == typeof Assertion.prototype[name]) {// 克隆原型上的方法var old = this[name];this[name] = function () {return old.apply(self, arguments);};// 因为当前是个函数对象,你要是在后面链式调用了Assertion原型上方法是找不到的。// 所以要将Assertion原型链上的所有的方法设置到当前的对象上for (var fn in Assertion.prototype) {if (Assertion.prototype.hasOwnProperty(fn) && fn != name) {this[name][fn] = bind(assertion[fn], assertion);}}} else {this[name] = assertion;}}}}

为什么要这样设计?我的理解是:首先expect.js的链式调用充分的体现了调用的逻辑性,而这种嵌套的结构真正的体现了各个修饰符之间的逻辑性。

所以我们可以这样书写:

  const student = {name: 'xiaoming',age: 20}expect(student).to.be.a('object')

当然这并没有完,对于每一个Assertion原型上的方法多会直接或者间接的调用assert方法:

  Assertion.prototype.assert = function (truth, msg, error, expected) {// 这就是flags属性的作用之一var msg = this.flags.not ? error : msg, ok = this.flags.not ? !truth : truth, err;if (!ok) {// 抛出错误err = new Error(msg.call(this));if (arguments.length > 3) {err.actual = this.obj;err.expected = expected;err.showDiff = true;}throw err;}// 为什么这里要再创建一个Assertion实例?也正是由于expect实例是一个嵌套对象。this.and = new Assertion(this.obj);};

并且每一个Assertion原型上的方法最终通过返回this来实现链式调用。所以我们还可以这样写:

  expect(student).to.be.a('object').and.to.have.property('name')

到此你应该已经理解了expect.js的链式调用的原理,总结起来就是两点:

  • 原型方法还是通过返回this,实现链式调用;
  • 通过嵌套结构的实例对象增强链式调用的逻辑性;

所以我们完全可以这样写:

  // 强烈不推荐 不然怎么能属于BDD风格呢?expect(student).a('object').property('name')

    喜欢本文的小伙伴们,欢迎关注我的订阅号超爱敲代码,查看更多内容.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366935.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java EE状态会话Bean(EJB)示例

在本文中&#xff0c;我们将了解如何在简单的Web应用程序中使用状态会话Bean来跟踪客户端会话中的状态。 1.简介 有状态会话Bean通常保存有关特定客户端会话的信息&#xff0c;并在整个会话中保留该信息&#xff08;与无状态会话Bean相对&#xff09;。 有状态EJB实例仅与一个…

计算机科学速成课16:软件工程

概念&#xff1a;建造标准或者大型软件的方法和工具代码打包成函数 面向过程函数打包成对象 面向对象对象层层打包Car.Engine.CruiseControl.SetCruiseSpeed(55)应用程序接口api集成开发环境IDE&#xff1a;code&#xff0c;整理&#xff0c;编译&#xff0c;测试注释和readme文…

牛客网NOIP赛前集训营-提高组(第六场)B-选择题[背包]

题意 题目链接 分析 直接背包之后可以 \(O(n)\) 去除一个物品的影响。注意特判 \([p1]\) 的情况。总时间复杂度为 \(O(n^2)\) 。代码 #include<bits/stdc.h> using namespace std; #define go(u) for(int ihead[u],ve[i].to;i;ie[i].last,ve[i].to) #define rep(i,a,b) f…

起点海外版 Hybrid App-内嵌页优化实践

本文作者&#xff1a;刘文涛 原创声明&#xff1a;本文为阅文前端团队 YFE 成员出品&#xff0c;请尊重原创&#xff0c;转载请联系公众号 (id: yuewen_YFE) 获取授权&#xff0c;并注明作者、出处和链接。 今年年初我司开启了起点品牌的海外之旅&#xff0c;名为「 Webnovel 」…

aix 卸载mysql_AIX 删除数据库及集群软件

一、 删除数据库1、用dbca自动删库在CRT上无法打开dbca图形界面&#xff0c;要安装一个Xmanage软件&#xff0c;用Xstart连接终端&#xff0c;并修改oracle用户的.profile&#xff0c;加上“export DISPLAY192.168.8.120:0.0”Xstart配置信息如下&#xff1a;2、手工删除数据库…

简单轻量级池实现

对象池是包含指定数量的对象的容器。 从池中获取对象时&#xff0c;在将对象放回之前&#xff0c;该对象在池中不可用。 池中的对象具有生命周期&#xff1a;创建&#xff0c;验证&#xff0c;销毁等。池有助于更好地管理可用资源。 有许多使用示例。 特别是在应用程序服务器中…

如何在github中的readme.md加入项目截图

1. 先在之前的本地项目文件夹里创建一个存放截图的文件夹。&#xff08;如img文件夹&#xff09; 2. 将新增的内容通过github desktop上传到github中 3. 在github中立马能看到刚刚上传的图片&#xff0c;打开图片&#xff0c;点击Download 4. 直接复制地址栏的网址 5. 最后在RE…

记表格设计规范整理与页面可视化生成工具开发

前言 公司有一个项目在维护&#xff0c;大概有300左右&#xff0c;其中表单与表格的页面占比大概百分之五六十&#xff0c;为了节省开发时间&#xff0c;避免多人协作时&#xff0c;出现多套冗余代码&#xff0c;我们尝试写了一下表单和表格的生成工具&#xff0c;从梳理到规范…

java仿qq空间音乐播放_完美实现仿QQ空间评论回复特效

评论回复是个很常见的东西&#xff0c;但是各大网站实现的方式却不尽相同。大体上有两种方式1.像优酷这种最常见&#xff0c;在输入框中要回复的人&#xff0c;这种方式下&#xff0c;用www.cppcns.com户可以修改。新浪微博则是在这个基础上&#xff0c;弹出好友菜单。这种方式…

使用签名保护基于HTTP的API

我在EMC上的一个平台上可以构建SaaS解决方案。 与越来越多的其他应用程序一样&#xff0c;该平台具有基于RESTful HTTP的API。 使用像JAX-RS这样的开发框架&#xff0c;构建这样的API相对容易。 但是&#xff0c; 正确构建它们并不容易。 建立基于HTTP的API的问题 问题不仅…

Python开发【模块】:Celery 分布式异步消息任务队列

前言&#xff1a; Celery 是一个 基于python开发的分布式异步消息任务队列&#xff0c;通过它可以轻松的实现任务的异步处理&#xff0c; 如果你的业务场景中需要用到异步任务&#xff0c;就可以考虑使用celery&#xff0c; 举几个实例场景中可用的例子: 你想对100台机器执行一…

iOS开发者的一些前端感悟

很多前端工程师会把自己比作“魔法师”&#xff0c;而对于JavaScript这门语言&#xff0c;我也想把它唤作一门“有魔力的语言”。因为这群有无限想法的人&#xff0c;真的在用它创造各种让你惊叹的事物。 Web三件套一、前言 几年前&#xff0c;笔者还是一名初涉编程的学生&…

windows下github 出现Permission denied (publickey)

github教科书传送门:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 再学习到"添加远程仓库"的时候遇到了 Permission denied (publickey) 这个问题&#xff0c; 总结来说以前的步骤如下所示&#xff1a; 1、git config --glo…

php如何逐条读取数据库,php从数据库中读取特定的行(实例)

有的时候我们需要从数据库中读取特定的数据&#xff0c;来检验用户的输入&#xff0c;这个时候需要执行的sql语句是&#xff1a;select * from table_name where idnum;需要执行这样的一个语句。那么怎样来执行这样的语句。为了执行sql语句&#xff0c;我们需要和数据库相连接$…

Java 7 vs Groovy 2.1性能比较

自从我与Grails上一次接触以来&#xff0c;我已经有两年没有使用Groovy了。 我陷入&#xff08;硬&#xff09;核心企业Java中&#xff0c;并且在后台遇到了一些性能方面的问题。 我几乎错过了学习Spock的机会&#xff0c;但是幸运的是&#xff0c; 华沙Java用户组帮助我摆脱了…

[UE4]嵌套Canvas

转载于:https://www.cnblogs.com/timy/p/9090642.html

写博客的这几个月,获益良多

1.前言 也将近过年了&#xff0c;看了那么多人搞了年会总结。自己活跃社区这几个月&#xff0c;改变了不少&#xff0c;收获也不少。就想写下这段时间写文章的一些总结&#xff0c;统计下‘成绩’&#xff0c;说下感想&#xff0c;就写了这篇文章。这次总结的关键词就是&#x…

php 注册自动登录,php – 创建第二个自动登录用户的登录页面

我有一个登录页面如下&#xff1a;Username: Password: LoginCancel在这里我的session.controller.php文件&#xff1a;基本上,我想要做的是创建第二个登录页面,自动将值传递给会话控制器并登录.例如,如果我转到login-guest.php,我会将用户名和密码的默认值和然后有一个jquery点…

Pyqt5+python+ErIC6+QT designer

Eric6安装及配置 https://blog.csdn.net/weixin_41656968/article/details/80253012 Python3.6PyQt5Eric6.0环境配置 https://blog.csdn.net/wf307388339wf/article/details/78990899 eric6新建工程编写代码操作步骤 https://jingyan.baidu.com/article/37bce2be5d29911003f3a2…

探索Apache Camel Core –文件组件

文件轮询器是解决常见IT问题的非常有用的机制。 Camel的内置file组件非常灵活&#xff0c;并且有许多选项可用于配置。 让我们在这里介绍一些常用用法。 轮询输入文件的目录 这是典型的骆驼Route用于每秒轮询一次目录以查找输入文件。 import org.slf4j.*; import org.apache…