这篇文章源于我上一周所读的一篇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. C#设计模式系列:适配器模式(Adapter)

    在实际的软件系统设计和开发中,为了完成某项工作需要购买一个第三方的库来加快开发.这带来一个问题,在应用程序中已经设计好的功能接口,与这个第三方提供的接口不一致.为了使得这些接口不兼容的类可以在一起工作 ...

  2. window下Nodejs的部署

    nodejs http://nodejs.org/#download npm node cli.js install npm -gf //可以安装最新版的NPM node cli.js install ...

  3. Hibernate的数据删除,更改

    其他未给出代码,请参考上一篇.... 一.数据的删除 方法1.从“多”的一方进行数据的删除 books.hbm.xml文件不变: <many-to-one name="publishe ...

  4. 使用ikmv将jar转换为dll,在.NET中直接调用

    ikmv百科介绍 ikvm官网 使用ikvm前需要先安装java. ikvm的使用比较简单,到官网下载ikvm需要的组件,解压后在path中配置 %IKVM_HOME%\bin 在cmd中使用命令 i ...

  5. 【Java心得总结四】Java泛型下——万恶的擦除

    一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...

  6. 解决CHROME中画布中无法显示图片的方法

    最终效果图如下 我按照W3SCHOOL里面的方法,代码如下 <!DOCTYPE html> <html> <body> <script type=" ...

  7. 浅尝ECMAScript6

    浅尝ECMAScript6 简介 ECMAScript6 是最新的ECMAScript标准,于2015年6月正式推出(所以也称为ECMAScript 2015),相比于2009年推出的es5, es6 ...

  8. spring in action 4th --- quick start

    读spring in action. 环境搭建 quick-start依赖注入 面向切面 1.环境搭建 jdk1.8 gradle 2.12 Intelij idea 2016.2.1 1.1创建一个 ...

  9. linux全方位掌握一个命令--思路比方法更重要

    Linux命令众多,当不清楚一个命令的使用方法时,我们该怎样了解命令的属性和帮助?     1.用type命令了解一个命令的属性 [root@zejin240 testdir]# type cd cd ...

  10. 利用jquery.validate以及bootstrap的tooltip开发气泡式的表单校验组件

    表单校验是页面开发中非常常见的一类需求,相信每个前端开发人员都有这方面的经验.网上有很多成熟的表单校验框架,虽然按照它们默认的设计,用起来没有多大的问题,但是在实际工作中,表单校验有可能有比较复杂的个 ...