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实例仅与一个…

起点海外版 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、手工删除数据库…

如何在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…

[UE4]嵌套Canvas

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

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

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

shiro 权限 URL 配置细节

转载于:https://www.cnblogs.com/hwgok/p/9100277.html

2016 年崛起的 JS 项目

本文是我对中文版 risingstars2016 的整理&#xff0c;而本人就是中文版的译者&#xff0c;首发于知乎专栏 前端周刊。共 21384 字&#xff0c;读完需 30 分钟&#xff0c;速读需 5 分钟。长江后浪推前浪&#xff0c;如果你能花 30 分钟读完我 6 个小时翻译的内容&#xff0c;相…

php 开启命令模式,如何启用PhpStorm中的命令行工具

本篇文章主要给大家介绍如何使用phpstorm中的命令行工具。PhpStorm下载地址&#xff1a;PhpStorm使用命令行工具&#xff0c;我们可以直接从IDE调用命令&#xff01;在我们使用任何命令行工具之前&#xff0c;我们必须在设置中启用它。涉及到的步骤如下&#xff1a;使用命令行工…

React Native项目自动化打包发布

今天这篇文章的目的是在rn项目的构建&#xff0c;并不会涉及到rn框架或者使用的讲解&#xff0c;说起构建&#xff0c;特别是前端构建大家应该很快会想到webpack、Grunt、 Gulp等。而这些工具在rn项目中就显得有些鸡肋。所以在此给大家分享一下不使用构建工具实现rn项目自动化打…

Python程序员之面试必回习题

写在前面 近日恰逢学生毕业季&#xff0c;课程后期大家“期待苦逼”的时刻莫过于每天早上内容回顾和面试题问答部分【临近毕业每天课前用40-60分钟对之前内容回顾、提问和补充&#xff0c;专挑班里不爱说话就的同学回答】。 期待的是可以检验自己学习的成功&#xff1b;苦逼的是…

SpringMVC原理MVC设计思想

什么是MVC&#xff1f; MVC是一种架构模式 --- 程序分层&#xff0c;分工合作&#xff0c;既相互独立&#xff0c;又协同工作 MVC是一种思考方式 --- 需要将什么信息展示给用户? 如何布局&#xff1f; 调用哪些业务逻辑&#xff1f; MVC流程图如下图所示&#xff1a; MVC核心思…

Hbase 的javaAPI基本操作用 在idea上的实现

1.保证集群开启&#xff1a; jps有如下进程 2.pom文件中的依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:sche…

旅行报告:JavaOne 2013 –重归荣耀

我已经回来几天了&#xff0c;需要赶上过去几天一直搁置的所有事情。 对我来说&#xff0c;这是一年中最忙的时间。 JavaOne和OpenWorld在旧金山的整整一周。 一个非常简短的旅行报告。 年度ACED简报 你们中许多人都知道我是Oracle社区认可计划&#xff08;称为“ ACE计划 ”&…