javascript的坑,javascript难不难

JavaScript 有什么坑

语法特性可能会成为入门者的坑。

为企业提供网站制作、成都网站制作、网站优化、营销型网站建设、竞价托管、品牌运营等营销获客服务。成都创新互联拥有网络营销运营团队,以丰富的互联网营销经验助力企业精准获客,真正落地解决中小企业营销获客难题,做到“让获客更简单”。自创立至今,成功用技术实力解决了企业“网站建设、网络品牌塑造、网络营销”三大难题,同时降低了营销成本,提高了有效客户转化率,获得了众多企业客户的高度认可!

比如:

-变量提升 (hoisting)

-闭包

-弱类型

JS位运算异常(位运算精度丢失)的原因探究

《【转+补充】深入研究js中的位运算及用法》

《【JS时间戳】获取时间戳的最快方式探究》

日常开发中一直没遇到过位运算导致精度丢失的问题,直到这天,搞10位时间戳取整的时候,终于被我撞上了。具体是个什么场景呢,我们来还原下案发现场:

可以看到输出的结果为:

得到的 t 是一个精确到微秒的时间戳。但是请求接口的时候需要的是一个10位(精确到秒)的时间戳,所以这里需要将它转换为10位,自然就是 ➗1000 即可,然后通过位运算来实现类似 Math.trunc 的取证效果,得到了我们要的10位时间戳。至此完美解决!那问题又是如何发生的呢?

按照上面的运算规律,如果我们要获取13位时间戳,是不是直接对 t0 就可以了呢?我们来看一下:

输出结果如下:

WTF!!!看到了咩!!!居然输出了一个负数!!!我们想要的结果应该是 1597113682985 才对啊!为什么会出现了负数呢!!!

由此,怪物出现啦!我们今天就来解读(xiang fu)一下它!

想到这里,我们一定就会怪是位运算的锅!那这个锅该怎么让位运算背起来呢!我们来研究一下!

首先我们知道,JS中没有真正的整型,数据都是以double(64bit)的标准格式存储的,这里就不再赘述了,要想搞透其中的原理,请打开 【传送门】

位运算是在数字底层(即表示数字的 32 个数位)进行运算的。由于位运算是低级的运算操作,所以速度往往也是最快的(相对其它运算如加减乘除来说),并且借助位运算有时我们还能实现更简单的程序逻辑,缺点是很不直观,许多场合不能够使用。

以下来源于w3shool:

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

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

可以以两种不同的方式存储二进制形式的有符号整数,一种用于存储正数,一种用于存储负数。 正数是以真二进制形式存储的 ,前 31 位中的每一位都表示 2 的幂,从第 1 位(位 0)开始,表示 20,第 2 位(位 1)表示 21。没用到的位用 0 填充,即忽略不计。例如,下图展示的是数 18 的表示法。

那在js中二进制和十进制如何转换呢?如下

负数同样以二进制存储,但使用的格式是二进制补码。计算一个数值的二进制补码,需要经过下列3个步骤:

例如,要确定-18的二进制表示,首先必须得到18的二进制表示,如下所示:

0000 0000 0000 0000 0000 0000 0001 0010

接下来,计算二进制反码,如下所示:

1111 1111 1111 1111 1111 1111 1110 1101

最后,在二进制反码上加 1,如下所示:

1111 1111 1111 1111 1111 1111 1110 1101 +

0000000000000000000000000000 0001 =

1111 1111 1111 1111 1111 1111 1110 1110

因此,-18 的二进制就是 1111 1111 1111 1111 1111 1111 1110 1110

而其相反数18的二进制为 0000 0000 0000 0000 0000 0000 0001 0010

ECMAScript会尽力向我们隐藏所有这些信息,在以二进制字符串形式输出一个负数时,我们看到的只是这个负数绝对值的二进制码前面加上了一个负号

JavaScript 只有一种数字类型 ( Number )

我们将 1596596596.3742654.toString(2) 转为二进制字符串表示如下:

1011111001010100010000101110100.0101111111001111110111

但实际在内存中的存储如下:

说到这里就不得不简单提一下数字精度丢失的问题。上面也知道,JS中所有的数字都是用double方式进行存储的,所以必然会存在精度丢失问题。

以下转自文章: JavaScript数字精度丢失问题总结

此时只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。

大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53) ,十进制即 9007199254740992

大于 9007199254740992 的可能会丢失精度:

9007199254740992 10000000000000...000 ``// 共计 53 个 0

9007199254740992 + 1 10000000000000...001 ``// 中间 52 个 0

9007199254740992 + 2 10000000000000...010 ``// 中间 51 个 0

实际上

9007199254740992 + 1 ``// 丢失

9007199254740992 + 2 ``// 未丢失

9007199254740992 + 3 ``// 丢失

9007199254740992 + 4 ``// 未丢失

以上,可以知道看似有穷的数字, 在计算机的二进制表示里却是无穷的,由于存储位数限制因此存在“舍去”,精度丢失就发生了。

想了解更深入的分析可以看这篇论文(你品!你细品!): What Every Computer Scientist Should Know About Floating-Point Arithmetic

关于精度和范围的内容可查看 【JS的数值精度和数值范围】

通过前面的知识补充,我们已经知道:

这也就是为什么对于整数部位为10位的时间戳,通过位运算可以进行取整(因为目前时间戳159xxxxxxx2147483647),不存在时间戳超过范围的问题。但是对于13位时间戳,如 15966154471232147483647 ,此时再通过位运算操作的时候就会导致异常,如:

这主要是因为在进行位运算之前,JS会先将64bit的浮点数 1596615447015.01 转为32bit的有符号整型后进行运算的,这个转换过程如下:

为了验证上述过程,我们再举一个例子: 1590015447015.123 0 = 877547495

将将将将!没错的吧!所以JS的这个坑还真是。。。 让人Orz

为什么javascript 这么多坑

javascript他爹说了,当初第一个版本就是花了一个星期搞出来的。人家是个lisp爱好者,但是用户喜欢C语言,所以搞出了这么个东西。很多现在说是坑的东西,其实都是他为了赶deadline随便弄出来的,没空细想。

后来流行了,想改也晚了。web程序员喜欢说,我们就是要快,哈哈哈哈哈哈,做基础设施怎么能求快呢,害人害己啊。

那些年趟过的OC与JS交互的坑

以前在项目中很少用到OC和JS交互,所以对这方面没有进行更加深入的了解,最近公司安排的一个项目;一半的内容是用h5写的;所以在这个过程中需要OC和JS进行无缝对接,互相频繁调用。在做的过程中出现了一些问题,这里总结出现,希望能给大家一些启示。

一.在webview中如果项目只是想简单用一下js和oc进行交互,就没有必要搞的那么麻烦,只需要用下面的方式进行处理就行.

1.oc调用js;例如,在js中有一个叫commit的方法,在oc中咱们可以这样来调用它

如果你调用的js函数需要传入参数,也不要紧,咱们只需要通过字符串拼接的方式将参数传入即可。比如当前有一个叫sum(a,b)的js函数,在oc中咱们可以这样来调用它---(注意:在内部参数要用' '来将参数括起来,这是因为html函数传参方式跟oc的不同,我们必须要按照html的传参方式传入才会有效果)

2.js调用oc;以前js调用oc是通过在webview这边拦截js那边触发的http请求的url地址来判断是不是我们需要调用的时机,然后再调用的oc。如果当我们在webview上点击一个按钮的时候他会跳转到这个页面,那么我们在oc这边就可以通过拦截这个地址做一些事情

二.其实,通过上面的方法是可以达到js和oc之间进行通讯,但是它也是存在一些弊端,或者说有时候不是很方便的。在ios7以后苹果官方将javascriptCore框架给开放出来了,我们这个通过这个框架就可以畅通无阻的让js和oc之间进行相互调用了。

1.通过javascriptCore来进行js和oc互相调用。第一步就是要进入javascriptCore这个框架.

之后就可以在需要使用的控制器中导入javascriptCore框架了,并且如果你需要js来调用oc,并且在js中的触发函数是native.submit(a,b)类型的,那么你需要将js方法和oc方法进行关联,这样处理以后,调用js对应的方法就会触发oc对应的方法,当然,JSContext对象也是必须的,这个对象是js和oc关联起来的工具,所有的js和oc调用都需要通过它来完成。

然后在webViewDidFinishLoad:中我们需要进行下面的步骤,关联native方法,是在当js方法中有native.submit()类型函数的时候才需要绑定的,如果没有这种类型的就不需要绑定。

注:其实通过上面的方法把self.context的native方法跟self绑定是会发生很严重的内存泄露问题的。因为这样会造成self被相互持有,导致该对象无法被释放,解决方案是native指定一个新的对象,然后在指定对象里实现JSExport协议。

接下来就是非native.submit()形式js触发函数调用oc的方式,我们这边可以用block形式关联javascript function.

通过上面的调用方式就可以获取到js调用时给oc传过来的参数,然后再进行相关处理.当js那边是native.类型的函数的时候,js调用oc,我们就可以直接拿到js跟oc绑定的那个方法来进行处理。

到此,js和oc之间相互调用的内容就差不多了,但是在使用的过程中有些情况下会发生莫名情况的野指针的情况 ,直接导致程序的崩溃。这个问题我是在webview中调用手机摄像头拍照上传的过程中发现的,具体流程是当需要上传图片的时候js调用oc的方法,然后oc进行拍照并且上传,当上传图片成功以后,再通过oc调用js通过js去回显图片,就是在回调js的过程中有时候就会发生莫名其妙的野指针崩溃,暂时我还没找到能够彻底解决这个问题的办法,等找到相关方法,我再进行公布。

续:之前在webView中频繁使用js和OC互相调用,出现了野指针问题,那么这个问题现在解决了。具体做法就是我们拿到webView所在的线程,让你要执行的方法在webView所在线程中执行。例如:

通过上述方法就可以避免野指针问题,但是具体是因为什么原因造成野指针问题的,我还没弄明白,希望知道的高手指教。


文章题目:javascript的坑,javascript难不难
文章分享:http://myzitong.com/article/dsgjejo.html