bigdecimal判断等于0_vue2.0源码用到的工具函数,12个简易的复用函数,看看有多简单...

戎马:https://segmentfault.com/a/1190000019679638

1. 创建一个被冻结的空对象

export const emptyObject = Object.freeze({ }) 

一旦创建不能给这个对象添加任何属性。

2. 判断是否是 undefinednull

function isUndef (v) { return v === undefined || v === null}

在源码中很多地方会判断一个值是否被定义,所以这里直接抽象成一个公共函数。

传入任意值,返回是一个布尔值。

3. 判断是否不是 undefinednull

function isDef (v) { return v !== undefined && v !== null}

当传入的值,既不是 undefined 也不是 null 返回true。

4. 判断是否是原始类型

function isPrimitive (value) { return ( typeof value === 'string' || typeof value === 'number' || typeof value === 'symbol' || typeof value === 'boolean' )}

在js中提供了两大类数据类型:

  1. 原始类型(基础类型):String、Number、Boolean、Null、Undefined、Symbol
  2. 对象类型:Object、Array、Function

5. 判断是否是对象类型

function isObject (obj: mixed) { return obj !== null && typeof obj === 'object'}

传入的值排除掉 null,因为在js中 null 使用运算符 typeof 得到的值是 object,这是一个 bug。因为历史原因放弃修复了。

6. 判断有效的数组下标

function isValidArrayIndex (val) { const n = parseFloat( String(val) ); // 转成数字 // 下标大于等于0,并且不是小数,并且是有限的数 return n >= 0 && Math.floor(n) === n && isFinite(val)}
  • 可以传入任意值,先调用 String 转成字符串,目的是防止传入的值为 Symbol 类型,那样直接调用 parseFloat 会报错,例如:
let test = Symbol('test');console.log(parseFloat(test))控制台捕获错误:Uncaught TypeError: Cannot convert a Symbol value to a string

原因是在调用 parseFloat 时,内部会调用内置的 ToString 方法。而内置的 ToString 方法在遇到 Symbol类型的值时,会抛出 TypeError 错误。

跟使用一些隐式转换遇到的问题一样,例如使用 + 号:

let test = '' + Symbol('text');控制台捕获错误:Uncaught TypeError: Cannot convert a Symbol value to a string

都是因为内部会调用内置的 ToString 方法造成的。

而如果手动调用 toString 方法或者调用 String,转换为字符串,则不会报错

let test = Symbol('test');console.log(test.toString()); // "Symbol(test)"console.log(String(test)) // "Symbol(test)"
  • 接下来判断 n >= 0 ,数组的下标不能小于0,这样就会排除掉小于0的数,以及 NaN
  • 并且 Math.floor(n) === n 一个数向下取整并且还等于自己,那只能是正整数,排除掉小数,因为数组的下标不能是小数。
  • 并且用 isFinite 来判定一个数字是否是有限数
console.log(isFinite(Infinity)); // falseconsole.log(isFinite(-Infinity)); // falseconsole.log(isFinite(123)); // true

7. 判断是否是一个 Promise 对象

function isPromise (val) { return ( isDef(val) && typeof val.then === 'function' && typeof val.catch === 'function' )}

当一个对象存在 then 方法,并且也存在 catch 方法,可以判定为 Promise 对象。

8. 删除数组中指定元素

这个方法有效的避免了进行删除数组某一项时,都要进行查找位置再删除的重复工作。

function remove (arr, item){ if (arr.length) { const index = arr.indexOf(item) if (index > -1) { return arr.splice(index, 1) } }}
  • 先判断数组长度,如果数组是空的,则没必要进行删除操作
  • indexOf 方法查找到元素在数组中的位置,如果找到返回元素所在的位置下标,如果不存在,则返回-1
  • index>-1 代表存在数组中,则调用 splice 进行删除,并返回删除的元素组成的数组,也就是 splice 的返回值。

9. 用做缓存的高阶函数

用高阶函数的好处是无需暴露不同要求的缓存对象在外面,形成一个闭包。下面这个函数的技巧,应用在工作中,可以提高代码运行的效率。

function cached(fn) { // 创建一个缓存对象 const cache = Object.create(null) return (function cachedFn (str) { // 先从缓存对象中找,要操作的值,是否已经有了操作结果 const hit = cache[str] // 如果有,则直接返回;没有,则调用函数对值进行操作,并把操作结果存在缓存对象中 return hit || (cache[str] = fn(str)) })}
  • 调用 cached 时会传入一个 fn 函数,这个函数对某些值进行操作,操作之后会产生返回值
  • cached 函数先定义一个没有原型的对象,会比用 {} 高效,因为不需要继承一大堆 Object.prototype 上的属性。
  • 执行完 cached 会返回一个函数 cachedFn,将来接收需要操作的值。函数 cachedFn 内部调用 fn 函数得到操作后的值,并缓存在对象 cache 中,如果再对同一个值进行操作时,则直接从缓存中取,无需再调用函数计算。

例如以下运用,函数的作用是把字符串的首字母大写。

const capitalize = cached((str) => { return str.charAt(0).toUpperCase() + str.slice(1)})
  • 先调用 cached 传入一个函数,这个函数是对字符串进行首字母大写的操作,并返回首字母大写的字符串结果,可以说创建了一个计算函数。
  • cached 的返回值是函数,也就是上面的 cachedFn 函数。

这时我们就可以调用 capitalize 对字符串进行首字母大写了。

capitalize('test'); // "Test"capitalize('test'); // "Test"capitalize('test'); // "Test"

第一次调用 capitalize 函数,先从缓存对象中取值,没有,则调用计算函数进行计算结果返回,同时存入缓存对象中。这时的缓存对象为:

{test: 'Test'}

再多次调用 capitalize 时,从缓存对象中取值,命中,直接返回,无需再进行计算操作。

10. 递归判断一个对象是否和另个一个对象完全相同

判断两个对象是否相同,主要是判断两个对象包含的值都是一样的,如果包含的值依然是个对象,则继续递归调用判断是否相同。

function isObject (obj){ return obj !== null && typeof obj === 'object'}function looseEqual (a, b) { // 如果是同一个对象,则相同 if (a === b) return true // 判断是否是对象 const isObjectA = isObject(a) const isObjectB = isObject(b) // 两者都是对象 if (isObjectA && isObjectB) { try { // 判断是否是数组 const isArrayA = Array.isArray(a) const isArrayB = Array.isArray(b) // 两者都是数组 if (isArrayA && isArrayB) { // 长度要一样,同时每一项都要相同,递归调用 return a.length === b.length && a.every((e, i) => { return looseEqual(e, b[i]) }) } else if (a instanceof Date && b instanceof Date) { // 如果都是时间对象,则需要保证时间戳相同 return a.getTime() === b.getTime() } else if (!isArrayA && !isArrayB) { // 两者都不是数组,则为对象 // 拿到两者的key值,存入数组 const keysA = Object.keys(a) const keysB = Object.keys(b) // 属性的个数要一样,递归的判断每一个值是否相同 return keysA.length === keysB.length && keysA.every(key => { return looseEqual(a[key], b[key]) }) } else {  return false } } catch (e) { return false } } else if (!isObjectA && !isObjectB) { // 两者都不是对象 // 转成字符串后,值是否一致 return String(a) === String(b) } else { return false }}
8023ce86d7c622447861faa3474f197e.png
087f49e7f76652600b319e703f213f7f.png
  • 判断两个值是否相同,无论是原始类型还是对象类型,如果相同,则直接返回true。
  • 如果两个都会对象,则分为两种情况,数组和对象。
  • 都是数组,则保证长度一致,同时调用 every 函数递归调用函数,保证每一项都一样
  • 是时间对象,则保证时间戳相同
  • 是对象,则先取出 key 组成的数组,两者 key 的个数要相同;再递归调用比较 value 值是否相同
  • 以上都不满足,直接返回false
  • 如果两者都不是对象,转成字符串后进行比较。
  • 以上都不满足,直接返回false

例子:

let a1 = [1,2,3,{a:1,b:2,c:[1,2,3]}];let b1 = [1,2,3,{a:1,b:2,c:[1,2,3]}];console.log(looseEqual(a1,b1)); // truelet a2 = [1,2,3,{a:1,b:2,c:[1,2,3,4]}];let b2 = [1,2,3,{a:1,b:2,c:[1,2,3]}];console.log(looseEqual(a2,b2)); // false

11. 函数只执行一次

同样利用高阶函数,在闭包内操作标识的真假,来控制执行一次。

function once (fn) { let called = false return function () { if (!called) { called = true fn.apply(this, arguments) } }}
  • 传入要执行一次的函数 fn
  • 设置标识为 false
  • 返回一个函数

实际运用:

function test(){ console.log('我只被执行一次');}let test2 = once(test);test2(); // 我只被执行一次test2();test2();test2();
  • 调用 once 函数后,会返回一个函数,赋值给 test2
  • 第一次调用 test2 后,在函数的尼内部,called 初次为 false, 所以可以执行函数 test,然后把标识 called 设置为true,就类似关闭了大门,下次不再执行。
  • 之后在调用 test2 , test 将不再执行。

12. 自定义 bind 函数

function polyfillBind (fn, ctx) { function boundFn (a) { const l = arguments.length return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx) } boundFn._length = fn.length return boundFn}
5822d23776842472c1ab7369b52d5d83.png

自定义的 bind 函数的场景,都是用来兼容不支持原生 bind 方法的环境。 在自己模拟的 bind 函数中,实际上调用的是 callapply

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

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

相关文章

告诉我们您想要什么,我们将做到:消费者驱动的合同测试消息传递

相当早以前,我们从REST(ful) Web API的角度讨论了消费者驱动的合同测试 ,尤其是将其投射到Java( JAX-RS 2.0规范)的角度。 可以公平地说,至少在公共API方面, REST仍在Web API领域占据…

宝塔面板php降级,宝塔面板6.8.8降级到5.9.1的方案教程

有朋友问起,6.8版本用的不习惯,而且缺少5.9版本的nginx过滤器,而且6.8版本许多功能阉割的厉害:服务器状态中的 连接管理、进程管理 也都被阉割,5.9版本是可以在面板中查看服务器进程管理的,如图&#xff1a…

c++矩阵连乘的动态规划算法并输出_「Javascript算法设计」× 动态规划与回溯算法...

目录:分而治之算法动态规划回溯算法分而治之算法分而治之算法是算法设计的一种方式,它将一个问题分成多个和原问题相似的小问题,递归解决小问题,再将解决方式合并以解决原来的问题(例如快速排序,二分搜索等…

背包问题九讲_背包问题

背包问题九讲我发现背包问题既棘手又有趣。 我敢肯定,如果您正在访问此页面,您已经知道了问题说明,但是只是为了完成本章: 问题: 给定一个最大容量为W和N的背包,每个背包都有自己的值和重量,将…

随机森林的特征 是放回抽样么_机器学习超详细实践攻略(10):随机森林算法详解及小白都能看懂的调参指南...

一、什么是随机森林前面我们已经介绍了决策树的基本原理和使用。但是决策树有一个很大的缺陷:因为决策树会非常细致地划分样本,如果决策树分得太多细致,会导致其在训练集上出现过拟合,而如果决策树粗略地划分样本,又不…

Java Code Geeks通过Twitter赠送了免费的Sublime Text Editor许可

正在与您的重量级编辑斗争? 然后,我们特别为您准备了一些东西! 我们正在举办比赛,免费赠送 kick-ass Sublime Text Editor 许可证 。 Sublime Text是用于代码,标记和散文的高级文本编辑器。 您会喜欢光滑的用户界面…

http端口_PhpStorm 修改默认端口号63342

PhpStorm 修改默认端口号63342​blog.csdn.net关键词PhpStorm phpStorm phpstorm 修改默认端口号63342 8080 80步骤1.打开Settings2. Build, Execution, Deployment→Deployment3.点击添加,选择In place起一个名字(可以随便起)在Web server U…

php根本自学不了,PHP开发自学还是培训?

员有两种,一种是会php的,一种是不会php的,今天我们不去说哪种开发语言好,哪种又不好,因为语言只是一种工具而已不管你是从IOS、.NET或其它语言进入这个行业,只要你在这个行业坚持多年,与php的感…

rds mysql 磁盘空间,RDS MySQL 空间问题的原因和解决

other_size- 系统文件和临时文件使用空间data_size- 数据文件使用空间binlog_size- Binlog 文件占用空间注:获取实例诊断报告的步骤请参考如何访问RDS 实例诊断报告。2. 解决RDS 实例支持单独升级磁盘空间,升级磁盘空间是解决空间问题的有效方式之一。下…

微信小程序 全局变量异步函数_微信小程序【生命周期】

小程序分为应用、页面和组件三个部分,所以小程序的生命周期涉及以下应用的生命周期页面的生命周期组件的声明周期应用的生命周期对页面生命周期的影响应用的生命周期App() 函数用来注册一个小程序。接受一个 Object 参数,其指定小程序的生命周期回调等。…

oracle agile 性能,Oracle Agile PLM安全漏洞(CVE-2016-3554)

Oracle Agile PLM安全漏洞(CVE-2016-3554)发布日期:2016-07-21更新日期:2016-07-25受影响系统:Oracle Supply Chain Products Suite 9.3.5Oracle Supply Chain Products Suite 9.3.4描述:CVE(CAN) ID: CVE-2016-3554Oracle Agile产…

java 挥发注解_Java的挥发性修饰符

java 挥发注解不久前,我写了一个Java servlet过滤器,该过滤器在其init函数中加载配置(基于web.xml的参数)。 筛选器的配置缓存在私有字段中。 我在字段上设置了volatile修饰符。 后来,当我检查Sonar公司以查看是否在代…

python 线程锁_python多线程编程(3): 使用互斥锁同步线程

问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系。现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1。很容易写出这样的代码&am…

Comnnect oracle,RAC监听日志与CRS日志

RAC监听日志:tnsname.oraCRS日志:位置:Oracle集群涉及的日志主要位于“$ORA_CRS_HOME/log”和“$ORACLE_HOME/log”目录中目录结构:tree -d $ORA_CRS_HOME/logtree -d $ORACLE_HOME/log禁止Clusterware在系统重启后自动启动&#…

python全栈开发百度云_价值2400 2016年11月全栈开发Flask Python Web 网站编程

01-计算机基础常识.mp4 02-Python语言概览、安装与运行.mp4 03-Python 变量、数据类型及存储.mp4 04-Python 常用数据类型概览.mp4 05-数值与字符串.mp4 06-列表list.mp4 07-字典表dict 与元组 tuple.mp4 08-文件与类型汇总.mp4 09-语句、表达式与if分支.mp4 10-循环语句.mp4 1…

Java / Spring:如何快速生成整个数据库CRUD REST API

随着时间的流逝,Spring框架已成为Java中使用最广泛的Web开发框架之一,这一点已变得显而易见。 在接下来的十年之际,Spring最受欢迎的模块Spring Boot刚刚进行了重大更新。 新的Spring Boot版本“ 2.2.0”和年份“ 2020”几乎完美匹配。 因此…

如何在虚拟linux环境运行python_Conda(Python)虚拟环境不能从Windows移植到Linux

在我的Windows 10计算机上,我使用以下命令创建了一个虚拟环境:>conda env export > environment.yml 我尝试在Windows系统上使用yml文件重新创建虚拟环境,效果很好。然后我转学了环境.yml在我的Linux机器(Ubuntu16.04.1&…

oracle两个表合并 sql,如何创建从两个表(Oracle DBMS)生成“合并”数据集的Select SQL语句?...

我最后做了两个步骤:第一步填充事件表1中的数据,第二步合并目标(第一步中的数据集)和另一个源之间的数据。请原谅我,因为法律原因,我不得不混淆表名并省略下面代码中的一些列。下面是SQL:INSERT INTO EVENTS_TARGET (VEHICLE_ID,EVENT_TYPE_ID,CLIENT_ID,EVENT_DATE,CREATED_DA…

php毕业设计遇到的问题,常见问题_php毕业设计_php课程设计_php大作业_原创作品下载网...

一、我们怎么交易?答:本站采用在线支付的方式,每个产品页面都有一个付款按钮,亲在线付款后页面会显示一个“订单号”,根据这个订单号在本站的下载页面(见顶部菜单)下载相应程序,注意,亲要在30分…

git maven 发布_Maven Git发布

git maven 发布在开始这篇文章之前,我需要指出我在去年才开始认真地与Git合作 。 不幸的是,我从事的许多项目仍在使用SVN或CVS,但现在我终于开始使用Git了 。 在过去的几年中,我使用Maven Release Plugin完成了许多软件发行。 我…