JavaScript - 如果...没有方法(xjl456852修改)

本文是对下面这篇文章中存在的错误进行修改,并增加少量注释.

原文出处:

JavaScript - 如果...没有方法

http://www.cnblogs.com/silin6/p/4367019.html

这篇文章源于我上一周所读的一篇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,于是我和基友想:如果连这些运算符都给干掉呢?只通过位运算来实现。
在各种恶补位运算的知识下,我的基友提出了另外一种解决方案:(xjl456852修改)

修改原因:

右移必须是31位才行,30位是错误的.-2147483648 >>30 你这个移动30就是-2不是-1, 移动31位才是-1.

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

简单的分析

觉得上面的代码逼格十足?那么让我们"粗略"的分析一下吧(详细计算、补码之类的知识请拉到参考引用)。
这些代码都运用了位运算——我们重点关照下~(按位取反运算)>>(有符号右偏移运算)

首先,偷点基础资料来:

重温整数

ECMAScript 整数有两种类型,即有符号整数(允许用正数和负数)和无符号整数(只允许用正数)。在 ECMAScript 中,所有整数字面量默认都是有符号整数。
有符号整数使用有 31 位表示整数的数值,从第1位开始数,第 32位表示整数的符号(下图是从第0位开始数,有些区别),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>>31 // -1 (感谢群里的@Superior和@Jeff Xiao提供)

过程如下:

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

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

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

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

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

代码总是很有意思的,没事玩玩代码放松一下自己也是好的,顺便还可以涨姿势,何乐而不为呢?顺便说一下,我和基佬商量着以后要是当了面试官就准备这个问题问一下别人——当然,只是娱乐娱乐。再次感谢群里的@Superior和@Jeff Xiao为我细心的讲解。
最后,向原文和前辈致敬:《JS,如果没有方法。。。(不借助任何JS方法实现round方法)》

JavaScript - 前端开发交流群:377786580

来源: http://www.cnblogs.com/silin6/p/4367019.html

null

时间: 2024-10-12 02:18:17

JavaScript - 如果...没有方法(xjl456852修改)的相关文章

javascript数组原型方法

1.javascript数组原型方法. 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>jstest</title> 6 </head> 7 <body> 8 <script> 9 var arr = ["1","2&q

javascript学习笔记---ECMAScriptECMAScript 对象----修改对象

通过使用 ECMAScript,不仅可以创建对象,还可以修改已有对象的行为. prototype 属性不仅可以定义构造函数的属性和方法,还可以为本地对象添加属性和方法. 创建新方法 通过已有的方法创建新方法Number.prototype.toHexString = function() { return this.toString(16); }; 在此环境中,关键字 this 指向 Number 的实例,因此可完全访问 Number 的所有方法.有了这段代码,可实现下面的操作: var iNu

深入了解javascript的sort方法

在javascript中,数组对象有一个有趣的方法 sort,它接收一个类型为函数的参数作为排序的依据.这意味着开发者只需要关注如何比较两个值的大小,而不用管"排序"这件事内部是如何实现的.不过了解一下sort的内部实现也不是一件坏事,何不深入了解一下呢? 算法课上,我们会接触很多种排序算法,什么冒泡排序.选择排序.快速排序.堆排序等等.那么javascript的 sort方法采用哪种排序算法呢?要搞清楚这个问题,呃,直接看v8源代码好了.v8中对 Array.sort的实现是采用ja

javascript 数组排序sort方法和自我实现排序方法的学习小结 by FungLeo

前言 针对一个数组进行排序,一个很常见的需求.尤其在后端.当然,前端也是有这个需求的. 当然,数组排序,是有现成的方法的.就是sort()方法. 我们先开看下这个. 标准答案,sort方法 var arr = [45,98,67,57,85,6,58,83,48,18]; console.log('原数组'); console.log(arr); console.log('sort方法从小到大排序'); console.log(arr.sort(function(a,b){return a-b}

JavaScript Array 数组方法汇总

JavaScript Array 数组方法汇总 1. arr.push() 从后面添加元素,返回值为添加完后的数组的长度 var arr = [1,2,3,4,5] console.log(arr.push(5)) // 6 console.log(arr) // [1,2,3,4,5,5] 2.arr.unshift() 从前面添加元素, var arr = [1,2,3,4,5] console.log(arr.unshift(2)) // 6 console.log(arr) //[2,1

html,JavaScript调用winfrom方法

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

Javascript 常用扩展方法

这篇文章纯粹是为了保存这些方法,供以后翻阅,其实一直保存在 evernote 里面,但觉得还是放到对的地方会好点. 现在收录的很少,希望以后会慢慢增多. 数组扩展 contains,remove 扩展 1 function ArrayContains(array, obj) { 2 for (var i = 0; i < array.length; i++) { 3 if (array[i] === obj) { 4 return true ; 5 } 6 } 7 return false ;

delegate实现Javascript的each方法

C#如何用delegate实现Javascript的each方法 C#中有很多易混淆的关键词,例如delegate,Func, Action和 Predicate.Func, Action和 Predicate本质上都是delegate,下面看一下delegate概念. 1 delegate概念 delegate本质上就是一个指向函数的指针,可以指向不同的函数,只要函数的签名和代理一致即可. 2 delegate应用 其实Func, Action, Predicate等都是delegate,只是

[13年迁移]javascript 的join(&quot;&quot;)方法,把数组变成统一字符串,用来写长的输出字符串

javascript 的join("")方法,把数组变成统一字符串,用来写长的参数字符串    function m(a) {        var b = ["<table class='DynarchCalendar-topCont'", j, "><tr><td>", "<div class='DynarchCalendar'>", e ? "<a clas