这篇文章源于我上一周所读的一篇12年的文章。原作者提出了一个问题,如果js没有原生方法Math.round(),我们如何去实现呢?

对此我和我的基友进行了小小探讨,并给出了一些有意思的答案。

> 本文内容如下:
> - 如果...没有方法
> - 解决方案
> - 另类解决方案
> - 简单的分析
> - 参考和引用

JavaScript - 前端开发交流群:377786580

如果...没有方法

这篇文章源于上周所读的一篇2012年的文章(为了强行塞点文章篇幅,所以把该文链接放到最后的引用了...希望原作者和读者体谅下....)。

原作者在使用了Math.round()方法之后,突然产生了一个小念头。

如果,js没有Math.round()方法,我们又该如何去实现呢?

为此展开了一些探讨。我知道发表这么一篇文章肯定小有争议,但仍需要注明的是这篇文章仅供娱乐,或者说——玩玩代码,不会太在意性能、健壮、逻辑严谨性等XXOO的东西。

Math.round()就是传说中的四舍五入啦...

        Math.round(12.1);//12
Math.round(12.8);//13
Math.round(-12.1);//-12
Math.round(-12.8);//-13

解决方案

原作者提供了这么些思路:

例如数字6.2,先把6.2转换为字符串,然后通过String.prototype.split()方法来分割字符串,判定字符串索引为1的值是否大于5,再处理索引为0的值,代码如下:

        //num===6.2
function round(num) {
var nums = String(num).split("."),//[6,2]
num0 = nums[0],//6
num1 = nums[1];//2 if (parseInt(num1.substring(0, 1)) < 5) { //2<5
return parseInt(num0);
} else {
if (num0 > 0) {
return parseInt(num0) + 1;
} else {//负数
return parseInt(num0) - 1;
}
}
}

原作者并不满意上面的解决方案,提出了如果连js原生方法都不使用呢?什么String()parseInt()都不使用该怎么去做呢?

于是提出了第二种解决方案:

这个问题的关键在于判定小数点后的数字是否大于5,所以我们把传递进来的数字6.2*10%10即可得到小数点后的数字,这时候再判定这个小数是否大于5即可。

        //num===6.2
function round(num) {
var round_x = (((10 * num) % 10) > 0) ?
((10 * num) % 10) : //正数
-((10 * num) % 10);//负数 if (round_x < 5) {
return num - (num % 1);//把小数点后的的数字干掉
} else {
return (num > 0 ?
(num - (num % 1) + 1) : //正数
num - (num % 1) - 1); //负数
}
}

原文只讲述到这里,后来我跟基友聊到了这篇文章,我的基友给出了另外一点思路:

因为是四舍五入原理,所以给当前的数字+0.5,得到的值直接丢弃小数点后面的部分转换为整数(类似parseInt),原来的数字也转换为整数丢弃小数点后面的部分,这两个数如果相差<1,表示取原来数字的整数,否则取新数字的整数。

        function round(num) {
var value = num > 0 ?
num + 0.5 : //正数
-(num - 0.5); //负数
value = value - value % 1;//得到新数的整数部分
//如果相差<1
return value - num < 1 ?
num - num % 1 :
value;
}

至此,稍微正常点的解决方案介绍完毕,下面我们来感受下什么叫做玩代码。

另类解决方案

听到基友的思路我表示非常赞非常好人民需要你代码需要你下一个图灵目测就是你了小伙子要不要买本《颈椎病康复指南》看看决定如何拯救世界?

然后给他感受了一下这个世界森森的恶意——也就是原文评论里的代码。

下面是欣赏代码时间,分析代码之类的肯定要放在后面。

        //@Gray Zhang的"给跪版",不支持负数
function round(x) {
return ~~(x + 0.5);
} //@Gray Zhang的"给跪加深版",支持正负数
function round(x) {
return ~~(x > 0 ? (x + 0.5) : (x - 0.5));
} //@强子~Developer的"请收下我的膝盖版"
function round(x) {
return (x > 0 ? x + 0.5 : x - 0.5) | 0;
}

看到这些代码当时我就给跪了,突然有种回家找家影楼给别人撒撒花,扬扬裙摆,送送快递的想法。好吧,我承认我的位运算就是个渣。

当然,你以为我们的思考仅限于此?no no no,我们觉得用这些什么ifelse三目运算符实在太low,于是我和基友想:如果连这些运算符都给干掉呢?只通过位运算来实现。

在各种恶补位运算的知识下,我的基友提出了另外一种解决方案:

        function round(x) {
return ~~(x + 0.5 + (x >> 30));
}

简单的分析

觉得上面的代码逼格十足?那么让我们"粗略"的分析一下吧(详细计算、补码之类的知识请拉到参考引用)。

这些代码都运用了位运算——我们重点关照下~(按位取反运算)>>(有符号右偏移运算)

首先,偷点基础资料来:

重温整数

ECMAScript 整数有两种类型,即有符号整数(允许用正数和负数)和无符号整数(只允许用正数)。在 ECMAScript 中,所有整数字面量默认都是有符号整数。

有符号整数使用 31 位表示整数的数值,用第 32 位表示整数的符号,0 表示正数,1 表示负数。数值范围从 -2147483648 到 2147483647。见下图:

因为样式的原因图片会存在拉伸,看不清请拿鼠标拽一下图片到新的浏览器标签页即可。

js中toString()方法可以to出二进制,而parsetInt()方法的第二个参数可以指定转换进制:

        (18).toString(2) //"10010"
parseInt(10010,2) //18

  

~的运算过程

~就是按位取反,类似:00111,取反则为11000

取反会干掉小数,~运算符的运算过程可以戳这里,我们看到调用了ToInt32()

所以会被干掉小数,所以我们可以这么来实现小数转整数:

        ~~18.5          //18 - 等同于parseInt(18)
parseInt(18.5) //18

~~是按位取反再取反,本质上就是一个干掉小数的过程。

  

>>有符号右移运算符

>>是有符号右移运算符由两个大于号表示(>>)。它把 32 位数字中的所有数位整体右移,同时保留该数的符号(正号或负号)。有符号右移运算符恰好与左移运算相反。

我们来解析一下这段代码:

    -2>>30 // -1 (感谢群里的@Superior和@Jeff Xiao提供)

过程如下:

  • 1 0000000000000000000000000000010 //-2二进制
  • 1 1111111111111111111111111111110 //-2进行补码
  • 1 1111111111111111111111111111111 //向右移动30,高位以符号位(第32位)补全
  • 1 0000000000000000000000000000001 //因为符号位为符号,所以是负数,则补码形式存储,还原为-1

  

我们再来看看我的基佬提供的代码:

        ~~(x + 0.5 + (x >> 30))
  • 假设X是-12.5
  • 首先,-12.5+0.5===-12
  • -12.5>>30:上面我们说过,ECMAScript有符号整数使用31位表示整数的数值,所以在ECMAScript中,任何一个数右移30位得到的结果只能是2种:正数得到0,负数得到-1。
  • -12-1===-13

由此完成了我们的运算,不得不说这个+0.5>>30很是精髓(虽然我基佬也是查了半天资料才搞出来 = =)。

再次声明,这篇文章和代码,纯属娱乐。对于上面看的迷迷糊糊,位运算之类的东西还搞不明白的童鞋可以看看下面的参考。

代码总是很有意思的,没事玩玩代码放松一下自己也是好的,顺便还可以涨姿势,何乐而不为呢?顺便说一下,我和基佬商量着以后要是当了面试官就准备这个问题问一下别人——当然,只是娱乐娱乐。再次感谢群里的@Superior和@Jeff Xiao为我细心的讲解。

最后,向原文和前辈致敬:《JS,如果没有方法。。。(不借助任何JS方法实现round方法)》

JavaScript - 前端开发交流群:377786580

参考和引用

JS,如果没有方法。。。(不借助任何JS方法实现round方法)

ECMAScript 位运算符

【译】从一行代码来学习JavaScript

javascript 中 !~ 什么意思

按位与(&)按位或(|)按位异或(^)按位取反(~)左移(<<)右移(>>)

Javascript小技巧,去掉小数位并且不会四舍五入

补码与求补运算(最基本也最容易忽略的东西)

MDN - parseInt

作者:linkFly
声明:嘿!你都拷走上面那么一大段了,我觉得你应该也不介意顺便拷走这一小段,希望你能够在每一次的引用中都保留这一段声明,尊重作者的辛勤劳动成果,本文与博客园共享。

JavaScript - 如果...没有方法的更多相关文章

  1. Javascript对象的方法赋值

    Javascript对象编程学习中,一直不能很好的掌握对象的属性(property)和方法(method).今天在写代码过程中,又犯了一个低级错误. <!DOCTYPE html> < ...

  2. JavaScript中reduce()方法

    原文  http://aotu.io/notes/2016/04/15/2016-04-14-js-reduce/   JavaScript中reduce()方法不完全指南 reduce() 方法接收 ...

  3. 两种动态加载JavaScript文件的方法

    两种动态加载JavaScript文件的方法 第一种便是利用ajax方式,第二种是,动静创建一个script标签,配置其src属性,经过把script标签拔出到页面head来加载js,感乐趣的网友可以看 ...

  4. 用jQuery之后,之前javascript的一些方法就不能用了吗

    用jQuery之后,之前javascript的一些方法就不能用了吗? 比如$("#btn").onclick = function(){}这种用法?或者$("#btn&q ...

  5. html,JavaScript调用winfrom方法

    ---恢复内容开始--- 目的: 在动画上面添加点击事件,通过JavaScript调用winfrom方法 1.创建一个页面 using System; using System.Collections ...

  6. delegate实现Javascript的each方法

    C#如何用delegate实现Javascript的each方法   C#中有很多易混淆的关键词,例如delegate,Func, Action和 Predicate.Func, Action和 Pr ...

  7. 在C#中模拟Javascript的setTimeout方法

    在C#中模拟Javascript的setTimeout方法 背景 每种语言都有自己的定时器(Timer),很多人熟悉Javascript中的setInterval和setTimeout,在Javasc ...

  8. JavaScript创建对象的方法

    显示在浏览器中的控制台中. <script type="text/javascript"> //这个工厂方法返回一个新的"范围对象" functio ...

  9. Javascript 面向对象(共有方法,私有方法,特权方法,静态属性和方法,静态类)示例讲解

    一,私有属性和方法 私有方法:私有方法本身是可以访问类内部的所有属性(即私有属性和公有属性),但是私有方法是不可以在类的外部被调用. <script> /* * 私有方法:私有方法本身是可 ...

  10. JavaScript中的方法、方法引用和参数

    首先,我们来看一段代码,如果觉得不甚明白的,则本文会对你有益: var player = function (e) {             return (function f(m) {      ...

随机推荐

  1. 模板引擎Nvelocity实例

    前言 最近一直忙于工作,没时间来管理博客,同时电脑也不给力,坏了一阵又一阵,最后还是去给修理了,这不刚一回来迫不及待的就写一篇文章来满足两个月未写博客的紧迫感. Nvelocity 关于nveloci ...

  2. T-SQL:毕业生出门需知系列(三)

    第3课 排序检索数据 3.1 排序数据(ORDER BY) 下面的 SQL 语句返回某个数据库表的单个列.观察其输出,并没有特定的顺序. SELECT prod_name FROM Products; ...

  3. Objective-C中的Block回调模式

    在前面的博客中提到了Block的概念和使用方法,个人感觉Block最爽的用法莫过于在回调时用block.感觉比委托回调和目标方法回调用着要顺手,好不好用还得读者亲自用一下才知道.如果 读者之前用过SS ...

  4. OracleDBA之表管理

    下面是Oracle表管理的部分,用到的测试表是oracle数据库中scott用户下的表做的测试,有的实验也用到了hr用户的数据,以下这些东西是我的麦库上存的当时学Oracle的学习笔记今天拿出来和大家 ...

  5. cf201.div1 Number Transformation II 【贪心】

    1 题目描述: 被给一系列的正整数x1,x2,x3...xn和两个非负整数a和b,通过下面两步操作将a转化为b: 1.对当前的a减1. 2.对当前a减去a % xi (i=1,2...n). 计算a转 ...

  6. c# 我所理解的 值类型 and 引用类型

    一直以来对于值类型和引用类型都只是一个模糊的概念,趁最近有空深入理解了下. 先说说值类型,在msdn上是这样介绍值类型的. 意思就是值类型直接包含值. 变量引用的位置就是值所在内存中实际存储的位置,所 ...

  7. C# 将excel表格嵌入到Word中

    C# 将excel表格嵌入到Word中 继续开扒,今天要实现的是使用C#将excel表格嵌入到Word中这个功能,将word表格导入到excel中我已经写过了,如有需要可参考我之前的文章,在开始前还有 ...

  8. xcode6 使用MJRefresh,Too many arguments to function call, expected 0, have *

    转载自:  http://blog.csdn.net/wsjshx/article/details/40743291 将XCode升级到6后,报Too many arguments to functi ...

  9. C语言 基础练习40题

    一.题目 1.输入2个整数,求两数的平方和并输出. 2. 输入一个圆半径(r)当r>=0时,计算并输出圆的面积和周长,否则,输出提示信息. 3.函数y=f(x)可表示为: 4.编写一个程序,从4 ...

  10. 第一篇blog

    之前不用blog,但是在杭电oj,poj上刷题,总会自己总结题型和使用什么算法,算法模板,自己在笔记本上写,耗时费力,感觉用键盘敲得总结,分享,大家相互学习提高.有时遇到不会做的,或者总是在oj上跑的 ...