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

一、前端中的断言

仔细地查找一下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')

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

JavaScript中不得不说的断言?相关推荐

  1. 为什么要在JavaScript中使用静态类型? (使用Flow进行静态打字的4部分入门)

    by Preethi Kasireddy 通过Preethi Kasireddy 为什么要在JavaScript中使用静态类型? (使用Flow进行静态打字的4部分入门) (Why use stati ...

  2. 详解Javascript中的Object对象

    本文地址:http://luopq.com/2016/02/28/Object-in-Javascript/,转载请注明 Object是在javascript中一个被我们经常使用的类型,而且JS中的所 ...

  3. JavaScript中的instanceof运算符是什么?

    本文翻译自:What is the instanceof operator in JavaScript? The instanceof keyword in JavaScript can be qui ...

  4. JavaScript中的私有成员

    JavaScript中的私有成员 Douglas Crockford www.crockford.com 翻译:ShiningRay @ Nirvana Studio JavaScript 是世界上最 ...

  5. 【前端工程师手册】说清楚JavaScript中的相等性判断

    有哪些判断相等性的方法 JavaScript现在提供了三种方法来判断相等性: ===,三个等号即严格相等 ==,两个等号即宽松相等 Object.is(),ES6中用来判断相等的方法 判断相等性的细节 ...

  6. JavaScript中错误正确处理方式,你用对了吗? 1

    JavaScript的事件驱动范式增添了丰富的语言,也是让使用JavaScript编程变得更加多样化.如果将浏览器设想为JavaScript的事件驱动工具,那么当错误发生时,某个事件就会被抛出.理论上 ...

  7. 在JavaScript中定义枚举的首选语法是什么? [关闭]

    在JavaScript中定义枚举的首选语法是什么? 就像是: my.namespace.ColorEnum = {RED : 0,GREEN : 1,BLUE : 2 }// later onif(c ...

  8. JavaScript 中 == 和 === 的区别

    1. 引言 这是在 JavaScript 中用来进行数值和对象对比时常用的操作符,从定义上来看: == :抽象相等,比较时会先进性类型转换,然后再比较值 === :严格相等,会比较两个值的类型和值 测 ...

  9. js学习笔记----JavaScript中DOM扩展的那些事

    什么都不说,先上总结的图~ Selectors API(选择符API) querySelector()方法 接收一个css选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null. ...

最新文章

  1. 打工皇帝杨元庆年薪达2136万美元 联想称很合理
  2. tcpdump + wireshark 抓包组合
  3. 调用接口登录禅道_有java调用api登录并验证禅道的实例吗
  4. 9203精英挑战赛注意事宜 一
  5. SpringSecurity-1-UserDetailsService接口
  6. Netbackup7.5 access to the client was not allowed(59)问题解决
  7. vue案例todolist备忘录
  8. 斐讯AI音箱怎么绑定扫地机器人_斐讯智能音箱常见问题解答
  9. mt7615 配置选项介绍
  10. Flowchart流程图和 Mermaid流程图的对比
  11. 高级数据结构——海量数据(位图,布隆过滤器)
  12. 【HTML总复习】一文带你查漏补缺,暖你一整天
  13. Android Room之数据库加密
  14. linux 证书文件权限,Linux运维之道之admin1.4(权限和归属,LDAP认证)
  15. 通达信指标公式编写常用函数(六)——SUM、IF
  16. python中0和1可以表示真假吗_教你用 python 辨别真假是非
  17. Qt编写安防视频监控系统(界面很漂亮)
  18. 毫米波频段射频器件的主要技术工艺趋势
  19. android驱动开发从零到一
  20. linux12 -MYSQL数据库 -->04 数据库和数据表基础命令--01

热门文章

  1. java描边_shape描边设置是否显示四周描边
  2. 华为mate40RS能升级鸿蒙,mate40Pro和40RS能用上鸿蒙系统吗
  3. 修改weblogic端口的方法
  4. 虚拟机环境下DPDK运行时的一些错误解决
  5. [luoguP1849] [USACO12MAR]拖拉机Tractor(spfa)
  6. windows服务器的DDOS防御,
  7. 《你的灯亮着吗》读后感1
  8. hadoop关联文件处理
  9. 程序员编程艺术第十一章:最长公共子序列(LCS)问题
  10. 产品经理经验谈100篇(二)-数据分析应用,如何构建指标体系?