原生JS | 当兔子遇到鸡
当兔子遇到鸡 —— 不要害怕和别人不一样,在这个世界上,你就是独一无二的自己!
不知道有多少程序在看到这个小视频的时候,想到的不是“复活节”彩蛋,而是“鸡兔同笼问题”……
如果你想到的是“鸡兔同笼”,那么恭喜你,至少你不是一个人……(表示看到兔子从蛋里钻出来的时候,竟然完全没有怀疑 )。
鸡兔同笼问题
鸡兔同笼-起源
“鸡兔同笼问题”是我国古算书《孙子算经》中著名的数学问题,其内容是:“今有雉(鸡)兔同笼,上有三十五头,下有九十四足。问雉兔各几何。”
前端实现鸡兔同笼的计算功能
功能特效:
如果你具有一定JS基础,不妨尝试书写一下或想一想,再看“功能实现”,涉及到的小细节知识可不少,特别是没有内容的对象({})的检测方法。
鸡兔同笼功能的基本实现
鸡兔计算的核心公式
兔子数量 = 脚数 / 2 - 头数;
鸡数量 = 头数 - 兔子数量。
Tips:当然,如果你考虑用for循环,从1只兔子到N只兔子,一个一个尝试组合,也并非不可,但是循环匹配的方法计算效率实在是太低了,并不推荐。另外,关于公式的“推导”过程,可详见下图:
核心功能函数
function caculate(head, foot) {
var result = {};
var rabbit = foot / 2 - head;
var chicken = head - rabbit;
if (rabbit * chicken > 0 && parseInt(rabbit) == rabbit) {
result = {
'rabbit' : rabbit,
'chicken' : chicken
}
};
return result;
}
注意,此处增加了一个if判断。
rabbit * chicken > 0 用于保证至少有一只鸡、一只兔(能够防止负值和0的出现)
parseInt(rabbit) == rabbit 用于防止小数的产生。
无论是0、负数、还是小数,针对这个实例都是不合法的,因此需要进行控制。在有合法结果时返回一个对象,而在没有合法结果时返回一个空的对象 - {}
对象的检测
如何检测一个没有内容的对象呢?这个问题反而成为了解决“鸡兔同笼”功能需求的关键。
码匠:如何判断一个对象是没有内容的?
码匠好友:判断{}是否等于{}?
码匠:{} == {} 是不可能成立的,虽然两个对象都是没有内容的,但是这是两个对象。所以该方法不可行……
码匠好友:让{}与null相比较?{}不是没有内容吗?
码匠:虽然{}对象当中没有内容,但是它并不等于null。所以该方法也是不可行的。
码匠好友:进行长度检测?
码匠:length属性可用于数组,但不能用于对象。
码匠好友:使用for-in循环进行枚举总可以了吧!(详见如下检测案例1)
码匠:枚举的确能够实现,但是太过繁琐,可以其他一些方法进行实现。(详见如下检测案例2、3)
检测案例1 - 利用for-in循环枚举
var obj = {};
function checkObj(obj) {
for(var i in obj) {
if(obj.hasOwnProperty(i)){
return false; // 不为空对象
}
}
return true; // 空对象,返回true
}
checkObj(obj);
相关说明:使用for-in循环进行枚举,对于没有内容的空对象({}),i值并不会被赋值,for-in中的内容也不会执行。
对于hasOwnProperty的检测,主要是让方法检测该属性是否是自身属性,而非原型链上(继承而来)的属性。
类似方法:使用Object.keys(obj);
Object.keys(obj); 该方法类似于枚举,但是,是直接调用Object的keys方法,方法的返回值为 —— 所有可枚举属性的字符串数组。如果当前对象为空({}),则返回一个空数组。
var obj = {};
var checkResult = Object.keys(obj);
console.log(checkResult.length);
检测案例2 - 利用JSON.stringify方法
var obj = {};
console.log(JSON.stringify(obj) == '{}')
JSON.stringify()方法,用于将JSON对象转换为字符串,当将对象转换为字符串之后,自然就可以和另一个字符串相比较了~~~
在本案例中,HTML5学堂(码匠)选用的是该种方法。
第3方法:getOwnPropertyNames
Object.getOwnPropertyNames()方法,返回对象中,除了原型属性之外的所有属性(包括不可枚举属性),返回的内容为数组,如果该对象没有属性,则返回空数组。
由于该方法返回值为一个数组,因此可以通过length属性,进行判断,如果length长度为0,则代表该对象为空。
var obj = {};
var checkResult = Object.getOwnPropertyNames(obj);
console.log(checkResult.length);
完整功能代码
结构代码
行为代码
涉及到的细节知识
字符串转换为数字
通过input获取到的值,类型为字符串,需要优先转换为数字,再进行操作,在本案例中HTML5学堂(码匠)选用的是Number方法。
NaN的基本检测
当用户在input中输入非数字或不合法数字时,会通过上面的Number方法转换为NaN。
当出现NaN时,则需要给用户给予反馈,而不是继续进行运算,因此需要进行NaN进行检测。
NaN检测的方法很简单,让其与自身相比较,如果自己和自己不相等,那么,该值为NaN。
整数检测
在鸡兔同笼计算当中,有可能用户输入的内容是合法数字,也是合法整数,但是有可能在运算之后变成小数。这种情况,是由于用户输入了不成立的数字组合。例如:头3只,脚7只。
对于此类问题,需要借助数字取整来检测,此处HTML5学堂(码匠)使用的是parseInt方法,针对兔子的数字取整,然后再跟原始的兔子数字比较,如果相同,则原始数字为整数。
没有内容的对象检测
在上面已经进行了详细讲解,在此罗列一下检测没有内容的对象的方法:
● JSON.stringify(obj);
● Object.keys(obj);
● Object.getOwnPropertyNames(obj);
查看效果实例:https://codepen.io/majiang/pen/oWoGmz
- [先行者课程] -- 用js实现倒计时功能的业务逻辑
- iOS如何实现多个环境一次打包
- 从原理到策略算法再到架构产品看推荐系统 | 附Spark实践案例
- MobX 在 React Native开发中的应用
- RCTEventEmitter使用
- Google V8 引擎
- 揭秘前端字符的戏精之路
- 跨语言嵌入模型的调查
- React Native如何消除启动时白屏
- 如何在Python中将TimeDistributed层用于Long Short-Term Memory Networks
- react native 调用原生UI组件
- Android侧滑删除另一种实现,SwipeListView补充
- React Native调用原生组件
- React Native调用原生UI组件
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- C# 纯控制台创建一个全屏窗口
- MySQL MHA部署添加Linux/Unix基本信息至Django中
- 记一次innobackupex导致的从库无法同步的问题
- mysqlbinlog命令详解记一次有函数的标量子查询导致的查询缓慢
- ctfshow红包题-web
- 微信支付一面(C++后台)
- 强网杯-随便注
- LeetCode 1547. Minimum Cost to Cut a Stick(动态规划)
- 管道符漏洞
- 06-STM32+ESP8266+AIR202远程升级方案-移植STM32+ESP8266实现利用http或https远程更新STM32程序到自己的项目(定时访问升级,备份升级)
- 反射
- 记一次sys 数据库的修复
- 使用Django管理数据库表
- php学习day3
- mysqlbinlog命令详解