0

0

守护 Javascript 中的函数参数

黄舟

黄舟

发布时间:2017-02-22 13:15:01

|

1480人浏览过

|

来源于php中文网

原创

作为开发者,我们花费许多时间来调试,尤其是在发现问题来源方面。开发工具指导我们追踪调用栈,但是追踪过程仍然相当耗时,尤其在遇到级联异步调用的时候。这一问题在很早以前就被发现了。

假设我们有一个从不同文档结构中搜索包含指定字符串的元素的函数。我们使用以下看起来合法的调用:

grep( "substring", tree );

但是我们并没有得到期望的结果。按照以往的经验,我们会花费一些时间来检查给定的树形文档结构,时间有可能会很长。然后我们很可能会做其他的检查,但是在最终,我们会从函数代码中发现传入的参数顺序反了。这样看来的话,我们只要关注函数的参数,就不会发生上面的错误。

function grep( tree, substring ){ if ( !( tree instanceof Tree ) ) { throw TypeError( "Invalid tree parameter" );

  } if ( typeof substring !== "string" ) { throw TypeError( "Invalid substring parameter" );

  } //... }

这种验证方式是 Design by Contract approach  的一部分。它在软件组成部分中列出了需要验证的前置条件和后置条件。在以上示例中,我们必须测试函数输入参数符合指定的格式(比较第一个参数符合树文档的类型,第二个参数符合字符串类型)同时我们建议检查函数输出类型是否是一个字符串。

但是,Javascript目前为止还没有其他语言那样内置的功能作为函数入口和结束处的验证。对于一个示例,PHP语言有类型提示:

TypeScript 有严格类型:

function grep( tree: Tree, substring: string ): string {}

此外,它还支持高级类型(联合类型,可选类型,交叉类型,泛型等等):

function normalize( numberLike: number | string, modifier?: boolean ): string {}

根据在ES规范中提出来得特性,今后会有一个叫做 Guards 的功能,它建议使用下面的语法:

function grep( tree:: Tree, substring:: String ):: String {}

目前为止在Javascript中,我们必须使用外部库或者可转换的编译器来解决这一问题。但是,可用的资源较少。最老的库是 Cerny.js  。它类似于DbC(数据库计算机),强大且灵活:

var NewMath = {};

(function() { var check = CERNY.check; var pre = CERNY.pre; var method = CERNY.method; // The new pision function pide(a,b) { return a / b;

    }

    method(NewMath, "pide", pide); // The precondition for a pision pre(pide, function(a,b) {

       check(b !== 0, "b may not be 0");

    });

})();

但是对我而言,它读起来很复杂。我更喜欢使用简洁干净的方式校验前提条件/后置条件即可。Contractual  提供的语法很符合我的要求:

function pide ( a, b ) {

  pre: typeof a === "number"; typeof b === "number";

    b !== 0, "May not pide by zero";

  main: return a / b;

  post:

    __result < a;

}

alert(pide(10, 0));

除了不是Javascript之外,看起来都很不错。如果你需要使用的话,必须用 Contractual或者 Babel Contracts 把源代码编译成Javascript。我不反对跨语言编译器,但是如果让我选择的话,我宁愿用 TypeScript。

但是回到Javascript,不知道你有没有发现,除了相关库和框架外,我们在注释函数和类的时候一直在用 JSDoc 描述函数入口和返回处的格式对比。如果文档注释可以用来验证格式的话就太好了。正如你所理解的,它离不开编译器。但是,我们可以使用依赖于Jascript文档表达式的库。幸运的是, byContract 就是这样的库。 byContract 的语法看起来像这样:

/**
 * @param {number|string} sum
 * @param {Object.} dictionary
 * @param {function} transformer
 * @returns {HTMLElement}
 */ function makeTotalElement( sum, dictionary, transformer ) { // Test if the contract is respected at entry point byContract( arguments, [ "number|string", 
 "Object.", "function" ] ); // .. var res = document.createElement( "p" ); // .. 
 // Test if the contract is respected at exit point return byContract( res, "HTMLElement" );
} // Test it var el1 = makeTotalElement( 100, { foo: "foo" }, function(){}); // ok var el2 = makeTotalElement( 100, { foo: 100 }, function(){}); // exception

如你所见,我们可以从文档注释处复制/粘贴指定的类型到 byContract 然后进行对比,就这么简单。下面我们更仔细地检查以下 。 byContract 可以被当做UMD模块(AMD或者CommonJS)或者全局变量来访问。我们可以把值 /Javascript 文档表达式作为一对参数传给 byContract

byContract( value, "JSDOC-EXPRESSION" );

或者值列表对应文档表达式列表作为一对参数也可以:

byContract( [ value, value ], [ "JSDOC-EXPRESSION", "JSDOC-EXPRESSION" ] );

byContract 会检测传入的值 ,如果和对应的 JSDoc 表达式格式不一致,就会抛出 带有像 ` 传入的值违反类型NaN`信息的 byContract.Exception 异常 。

在最简单的案例中,byContract用来验证如 `array`, `string`, `undefined`, `boolean`, `function`, `nan`, `null`, `number`, `object`, `regexp`之类的 原型类型:

byContract( true, "boolean" );

当我们需要允许输入值在一个指定类型列表中的时候,可以使用 type union 。

byContract( 100, "string|number|boolean" );

一个函数可以有必填的参数,也可以有可选参数。默认情况下,参数在和原型类型做对比的时候是必填的。但是用'='修饰符我们就可以设置成可选类型。所以 byContract 处理如 `number=` 这样的表达式时候,会转为 `number|undefined`

function foo( bar, baz ) {
  byContract( arguments, [ "number=", "string=" ] );
}

下面是Js文档中 nullable/non-nullable types (可空/不可空类型):

byContract( 42, "?number" ); // a number or null. byContract( 42, "!number" ); // a number, but never null.

当然,我们可以用接口来做比较。这样我们就可以引用作用域范围内任何可用的对象,包括Javascript内置接口:

var instance = new Date();
byContract( instance, "Date" );
byContract( view, "Backbone.NativeView" );
byContract( e, "Event" );

对于数组和对象,我们可以有选择性地验证其内容。比如可以验证所有数组的值必须是数字或者所有的对象的键和值是字符串类型:

byContract( [ 1, 1 ], "Array." );
byContract( { foo: "foo", bar: "bar" }, "Object." );

以上的验证对线性数据结构有用,其他情况下就不起作用了。所以同样的,我们可以创建一个 type definition (类型定义)来描述对象的内容(参考byContract类型定义)然后在后面作为一个类型引用它即可。

byContract.typedef( "Hero", {
  hasSuperhumanStrength: "boolean",
  hasWaterbreathing: "boolean" }); var superman = {
  hasSuperhumanStrength: true,
  hasWaterbreathing: false };
byContract( superman, "Hero" );

这个示例定义了一个'Hero'类型来表示一个对象/命名空间,必须有boolean类型的 `hasSuperhumanStrength`和`hasWaterbreathing` 属性。

所有的方法都通过类型验证传入的值,但是不变的量(常量)呢?我们可以用一个自定义类型来包装类型约束。比如说检测字符串是不是一个邮件地址类型,我们可以增加这样的验证:

byContract.is.email = function( val ){ var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|
(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test( val );
}
byContract( "john.snow@got.com", "email" ); // ok byContract( "bla-bla", "email" ); // Exception!

事实上,你很可能不要用事件来写验证函数,而是用外部库(类似 validator )代替:

byContract.is.email = validator.isEmail;

验证逻辑取决于开发环境。使用 byContract, 我们可以用全局触发器来禁用验证逻辑 :

if ( env !== "dev" ) {
  byContract.isEnabled = false;
}

byContract 是一个很小的验证插件(压缩文件大约1KB大小) ,你可以在你的Javascript代码中使用它从而得到对比编程设计模式的好处。

 

以上就是守护 javascript 中的函数参数的内容,更多相关内容请关注php中文网(www.php.cn)!

佳蓝智能应答系统
佳蓝智能应答系统

类似智能机器人程序,以聊天对话框的界面显示,通过输入问题、或点击交谈记录中的超链接进行查询,从而获取访客需要了解的资料等信息。系统自动保留用户访问信息及操作记录。后台有详细的设置和查询模块。适用领域:无人职守的客服系统自助问答系统智能机器人开发文档、资源管理系统……基本功能:设置对话界面的显示参数设置各类展示广告根据来访次数显示不同的欢迎词整合其他程序。

下载


相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

2

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

2

2026.01.29

java成品学习网站推荐大全
java成品学习网站推荐大全

本专题整合了java成品网站、在线成品网站源码、源码入口等等相关内容,阅读专题下面的文章了解更多详细推荐内容。

0

2026.01.29

Java字符串处理使用教程合集
Java字符串处理使用教程合集

本专题整合了Java字符串截取、处理、使用、实战等等教程内容,阅读专题下面的文章了解详细操作教程。

0

2026.01.29

Java空对象相关教程合集
Java空对象相关教程合集

本专题整合了Java空对象相关教程,阅读专题下面的文章了解更多详细内容。

3

2026.01.29

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

25

2026.01.29

clawdbot龙虾机器人官网入口 clawdbot ai官方网站地址
clawdbot龙虾机器人官网入口 clawdbot ai官方网站地址

clawdbot龙虾机器人官网入口:https://clawd.bot/,clawdbot ai是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

16

2026.01.29

Golang 网络安全与加密实战
Golang 网络安全与加密实战

本专题系统讲解 Golang 在网络安全与加密技术中的应用,包括对称加密与非对称加密(AES、RSA)、哈希与数字签名、JWT身份认证、SSL/TLS 安全通信、常见网络攻击防范(如SQL注入、XSS、CSRF)及其防护措施。通过实战案例,帮助学习者掌握 如何使用 Go 语言保障网络通信的安全性,保护用户数据与隐私。

8

2026.01.29

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

622

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.3万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号