JS魔法堂:再识Bitwise Operation & Bitwise Shift
Brief
linkFly的《JavaScript-如果...没有方法》中提及如何手写Math.round方法,各种奇技淫招看着十分过瘾,最让我惊叹的是 ~~(x + 0.5 + (x >> )) ,完全通过加法和位运算搞定整数的四舍五入。在好奇心的驱使下重温了一下位运算,并对上述公式加以封装得到适合小数的四舍五入方法
function round(v/*alue*/, p/*recision*/){
p = Math.pow(, p>>> ? : p|)
v *= p
return (v + 0.5 + (v>>)|) / p
}
在开波前我们先要了解一个现实,那就是虽然JS仅有Number这个数值类型,并且Number底层采用IEEE 754 64bit Double precision floating-point编码,但JS中实际上还是存在Signed Int32、Unsigned Int32和Unsigned Int16的数值编码方式,只是它们仅存在于运算过程中而已,而按位运算则是其中之一。
Bitwise Operation
NOT Operation
取反操作,符号为~, ~=、~= 。
JS的底层实现:~ToInt32(GetValue(expr))
由于Signed Int32采用补码方式编码,因此会存在对n取反后结果等于-n-1,即~n=-n-1。
Bitwise OR
或操作,符号为|, |=、|=、|= 。
JS的底层实现:ToInt32(GetValue(oprand1)) | ToInt32(GetValue(oprand1))
Bitwise AND
与操作,符号为&, &=、&=、&= 。
JS的底层实现:ToInt32(GetValue(oprand1)) & ToInt32(GetValue(oprand1))
Exclusive OR
异或操作,符号为^, ^=、^=、^= 。
JS的底层实现:ToInt32(GetValue(oprand1)) ^ ToInt32(GetValue(oprand1))
Bitwise Shift
Arithmetic Shift
Signed Right Shift Operator
有符号右移操作符,符号为>>。
JS的底层实现:ToInt32(GetValue(oprand1)) >> (ToUint32(GetValue(oprand2)) & 0x1F)。
示例:0111>>3,得到0000;1001>>3,得到1111
注意:由于Int32采用补码形式存储,因此 正数>>31 得到0,而 负数>>31 得到 -1。
Signed Left Shift Operator
有符号左移操作符,符号为<<。
JS的底层实现:ToInt32(GetValue(oprand1)) << (ToUint32(GetValue(oprand2)) & 0x1F)。
示例:0111<<3,得到0000;1001<<3,得到1100
Logical Shift
Unsigned Right Shift Operator
无符号右移操作符,符号为>>>。
JS的底层实现:ToInt32(GetValue(oprand1)) >>> (ToUint32(GetValue(oprand2)) & 0x1F)。
示例:0111>>>3,得到0000;1001>>>3,得到0001
注意:由于Int32采用补码形式存储,因此 正数>>>31 得到0,而 负数>>>31 得到 1。
Abstract Operations
[[DefaultValue]](hint)
用于获取对象的PrimitiveValue。具体规则如下:
hint为string或hint为空,且对象类型为Date object时:
1. 调用toString方法,若返回值为primitive value则直接返回;
2. 调用valueOf方法,若返回值为primitive value则直接返回;
3. 抛出TypeError实例。
hint为number或hint为空,且对象类型不为Date object时:
1. 调用valueOf方法,若返回值为primitive value则直接返回;
2. 调用toString方法,若返回值为primitive value则直接返回;
3. 抛出TypeError实例。
ToPrimitive(input[, preferredType])
用于获取入参input的PrimitiveValue。具体规则如下:
1. 若入参input的类型为Undefined,Null,Boolean,Number,String都直接获取其[[PrimitiveValue]];
2. 其他情况则调用input的[[DefaultValue]](preferredType)方法
ToNumber
用于将其他数据类型转换为Number type。具体规则如下:
1. Undefined -> NaN
2. Null -> +0
3. Boolean,true -> 1
false -> 0
4. Object,ToNumber(ToPrimitive(arg, hint-number))
5. String,对于无法解析为StringNumericLiteral的字符串则返回NaN
StringNumericLiteral与NumericLiteral的区别:
a. 前后可以有多个空格符号或LineTerminator;
示例: Number("\r\n 123\r\r\n ") 结果为
b. 前面可以有N个0,依然以十进制来解析字符串;
示例: Number("") 结果为 ,而var num = 0123则是以八进制表示83
c. 若指定符号,则符号后面要紧跟数值。否则会返回NaN;
示例:
Number("- 123"); //结果为 NaN
Number("-123"); // 结果为-123
var nl = - ;console.log(nl); //结果为-123
var nl = - + ;console.log(nl); //结果为-123
d. 若StringNumericLiteral仅含LineTeminator和WhiteSpace,则返回0。
LineTerminator包含 <LF>(换行符,\n,U+000A)
<CR>(回车符,\r,U+000D)
<LS>(Unicode中的行分隔符,U+2028)
<PS>(Unicode中的段落分隔符,U+2029)
WhiteSpace包含 ASCII的空白字符
<TAB>(缩进TAB符,\t,U+0009)
<VT>(垂直缩进TAB符,\v,U+000B)
<FF>(分页符,\f,U+000C)
<SP>(普通空格符,U+0020)
<NBSP>(非断行空格符,U+00A0)
<BOM>(bit order mark,Unicode中的零宽非断行空格,U+FEFF)
作用:作为UTF格式编码的文件的首个字符,用于程序在解析该文件时猜测采用的是采用哪种UTF编码方式。
<USP>(Unicode中的所有空白字符)具体看http://www.cnblogs.com/winter-cn/archive/2012/04/17/2454229.html
ToInteger([value])
具体规则如下:
function ToInteger(value){
var num = ToNumber(value)
if (Number.isNaN(num)) return
if (num === || !Number.isFinite(num)) return num
return sign(num)*floor(abs(num))
}
ToInt32([value])
具体规则如下:
function ToInteger(value){
var num = ToNumber(value)
if (Number.isNaN(num)) return
if (num === || !Number.isFinite(num)) return num
var mod = <<
num = sign(num)*floor(abs(num))
num = num - mod * floor(num/mod)
if (num > <<){
num = ~(num & ->>>>>) +
}
return num
}
ToUint32([value])
具体规则如下:
function ToInteger(value){
var num = ToNumber(value)
if (Number.isNaN(num)) return
if (num === || !Number.isFinite(num)) return num
var mod = <<
num = sign(num)*floor(abs(num))
num = num - mod * floor(num/mod)
return num
}
ToUint16([value])
具体规则如下:
function ToInteger(value){
var num = ToNumber(value)
if (Number.isNaN(num)) return
if (num === || !Number.isFinite(num)) return num
var mod = <<
num = sign(num)*floor(abs(num))
num = num - mod * floor(num/mod)
return num
}
Usage
说了这么多还是不如直接看疗效吧
//奇偶判断
function isEven(val){
return !(val&)
}
function isOdd(val){
return !!(val&)
} // 字符串是否含某字符判断
function contains(str, c){
return !!~str.indexOf(c)
} // 正负号判断
function isPos(val){
return !(val>>)
}
function isNeg(val){
return !!val>>>
} // 掩码
function getGroup(ip, mask){
return (ip&mask).toString()
} // 大小写转换
function toLowerCase(ll){
return String.fromCharCode(ll.charCodeAt()|<<)
}
function toUpperCase(ul){
return String.fromCharCode(ul.charCodeAt()&((->>>25)^(<<5)))
}
Take Action
回到最初四舍五入法方法,其中利用位运算的就两个部分, p>>> ? : p| 和 v + 0.5 + (v>>)| 。
p>>>31用于判断p的正负号,若p为正数则返回0,若p为负数则返回1;而p|0则用于截取p的整数部分。
0.5 + v>>31实质是用于令0.5与v具有相同符号而已,v>>31若v为整数则返回0,若v为负数则返回-1。
Conclusion
也许在日常工作中确实很少使用按位运算,大概有三个原因吧:
1. 确实没这个需求;
2. 有这个需求但不会用;
3. 有这个需求而且会用,但其他同事不懂导致可维护性“低”。
但不管用到与否,理解个中原理还是很爽的!
尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/5142200.html^_^肥子John
Thanks
http://www.cnblogs.com/winter-cn/archive/2012/04/17/2454229.html
http://es5.github.io
http://www.cnblogs.com/silin6/p/4367019.html
JS魔法堂:再识Bitwise Operation & Bitwise Shift的更多相关文章
- JS魔法堂:不完全国际化&本地化手册 之 实战篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:jsDeferred源码剖析
一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计& ...
- JS魔法堂:属性、特性,傻傻分不清楚
一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...
- JS魔法堂:那些困扰你的DOM集合类型
一.前言 大家先看看下面的js,猜猜结果会怎样吧! 可选答案: ①. 获取id属性值为id的节点元素 ②. 抛namedItem is undefined的异常 var nodes = documen ...
- JS魔法堂:doctype我们应该了解的基础知识
一.前言 什么是doctype?其实我们一直使用,却很少停下来看清楚它到底是什么,对网页有什么作用.本篇将和大家一起探讨那个默默无闻的doctype吧! 二.什么是doctype doctype或DT ...
- JS魔法堂:精确判断IE的文档模式by特征嗅探
一.前言 苦逼的前端攻城狮都深受浏览器兼容之苦,再完成每一项功能前都要左顾右盼,生怕浏览器不支持某个API,生怕原生API内含臭虫因此判断浏览器类型和版本号成了不可绕过的一道关卡,而特征嗅探是继浏览器 ...
- JS魔法堂:追忆那些原始的选择器
一.前言 ...
- JS魔法堂:不完全国际化&本地化手册 之 理論篇
前言 最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...
- JS魔法堂:判断节点位置关系
一.前言 在polyfill querySelectorAll 和写弹出窗时都需要判断两个节点间的位置关系,通过jQuery我们可以轻松搞定,但原生JS呢?下面我将整理各种判断方法,以供日后查阅. 二 ...
- JS魔法堂:LINK元素深入详解
一.前言 我们一般使用方式为 <link type="text/css" rel="stylesheet" href="text.css&quo ...
随机推荐
- ASP.NET 4.5.256 has not been registered on the Web server. You need to manually configure your Web server for ASP.NET 4.5.256 in order for your site to run correctly
Microsoft .NET Framework 4.6安装后,用户可能会在使用Microsoft Visual Studio 创建(或打开现有项目时)网站.或Windows Azure项目时遇到下面 ...
- Xamarin 跨移动端开发系列(01) -- 搭建环境、编译、调试、部署、运行
如果是.NET开发人员,想学习手机应用开发(Android和iOS),Xamarin 无疑是最好的选择,编写一次,即可发布到Android和iOS平台,真是利器中的利器啊!好了,废话不多说,就开始吧, ...
- 图解集合3:CopyOnWriteArrayList
初识CopyOnWriteArrayList 第一次见到CopyOnWriteArrayList,是在研究JDBC的时候,每一个数据库的Driver都是维护在一个CopyOnWriteArrayLis ...
- WebDAV 配置及相关工具
最近在项目中安装和调试服务器,杯具的是,服务器是内网地址,而且不可以直接SSH.SFTP,只能通过中间一台linux作为跳板,然后在SSH命令行里去操作目标机器. 如果只是命令行操作也就无所谓了,但是 ...
- JavaScript思维导图—函数基础
JavaScript思维导图-来自@王子墨http://julying.com/blog/the-features-of-javascript-language-summary-maps/
- Oracle存在修改,不存在插入记录
接触编程以来,在数据存储方面一直用的MS SQL.Oracle这名字对我来说是如此的熟悉,但是对其内容却很陌生,最近公司的一个项目用起了Oracle,所以也开始高调的用起了Oracle.在没有接触Or ...
- Unity3D热更新全书FAQ
只要有程序员朋友们问过两次的问题 就会收录在此FAQ中 1.C#Light对比LUA有什么好处 C#Light是静态类型脚本语言,语法同C#,Lua是动态类型脚本语言,这两种都有人喜欢. 我更喜欢静态 ...
- Android获取View对应的Bitmap
我的应用里面有一个需求,将一个画面分享出去,这个画面底层是一个View,所以首先要把这个View转换成Bitmap,然后在分享这个bitmap即可.话不多说,直接上代码. 有个地方需要注意一下:就是/ ...
- HTML5本地存储——IndexedDB(二:索引)
在HTML5本地存储——IndexedDB(一:基本使用)中介绍了关于IndexedDB的基本使用方法,很不过瘾,这篇我们来看看indexedDB的杀器——索引. 熟悉数据库的同学都知道索引的一个好处 ...
- NSDate NSString相互转化
时间戳是经常用到的,今天就总结一下 //设置转化格式 NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter s ...