浅谈js的诡异

830 阅读6分钟

javascript是建立在一些非常好的想法和少数非常坏的想法之上。这就要求js程序员要学会取其精华,避其糟粕。从而提高js代码的质量与可读性,避免一些潜在的隐患。 这里笔者主要是浅谈一下js的一些糟糕特性,以及如何避免它的方法。

js糟糕特性

全局变量

  • 来源:由于js早期只是作为写页面小脚本的语言存在,因此没有预想到其作为前端主流语言发展趋势。不同模块js不可能在同一个文件中存在,更别说会打包成一个项目的形式存在。所以本着追求更快更便捷的方式发明出来的js早期对于全局变量依赖相当严重。
  • 问题:全局变量的存在,导致最严重的问题就是由于其位于作用域链的顶端。所有开发人员无论在那个位置都能拿到该变量并且对其进行读写。并且当项目庞大之后,极其容易出现前一个开发人员声明某一个变量,后一个开发人员就声明相同变量将其覆盖掉了。这两种现象都容易产生极大的隐患。
  • 解决方案:目前主流是引入模块化方式进行开发,避免变量命名冲突。在编写过程中采用es6块级声明方案,尽量不要对一些已存在全局变量进行修改。
  • 总结:随着es6的普遍,以及cmd、amd、import/export的流行。以上现象或许很少会出现,但这些解决方案,都是前辈们的一脚一个坑踩出来的,我们是没必要再去踩这些坑的。

自动插入分号

  • 来源:由于js是以脚本语言的角色而存在。因此对于压缩的js代码,其有一个机制就是会尝试自动插入分号来修正有缺损的程序。这个特性,对于一些喜欢代码简短的程序员或许是福音,但是这个机制其实容易产生更大的错误。
  • 现象:
if(condition) 
    actuating logic1
    actuating logic1
    • 这种代码或许确实简短。但其实这无论是对于可读性,或者代码规范都是很low的一种写法。
  • 解决方案:在实际编写过程中,对于一切容易产生歧义的代码现象都要养成洁癖习惯。

Unicode

  • 来源:因为在JavaScript语言出现的时候,还没有UTF-16编码,因此只能采用UCS-2这种编码方案。造成所有字符在这门语言中都是2个字节,如果是4个字节的字符,会当作两个双字节的字符处理。
  • 现象:以字符为例,它的UTF-16编码是4个字节的0xD834 DF06。问题就来了,4个字节的编码不属于UCS-2,JavaScript不认识,只会把它看作单独的两个字符U+D834和U+DF06。前面说过,这两个码点是空的,所以JavaScript会认为是两个空字符组成的字符串!
  • 解决方案:在es5时期及以前,只能程序员自己编写函数对字符进行判断过滤。es6之后,对其进行了针对性的解决,基本上可以说解决了这个问题。但我们面对一些es5相关代码的时候还是有必要多一层意识的。

typeof

  • 来源:在js最初版本中,使用的是32位系统。为了使其性能上得到提高,使用低位来存储变量类型信息。节省内存上的使用。
    • 类型-标识关系如下
数据类型机器码标识
Object000
int1
float010
String100
Boolean110
undefined-2^31(二进制全为1)
null全为0
  • 现象:typeof null结果为Object。然而null instanceof Object结果是false。
  • 解决方案:该问题的出现其实源于历史原因。如果了解原型链的读者,会发现原型链的顶端就是null。因此null就算跟Object有关系,也是其跟Object.prototype的关系。可以根据原型链原理使用instanceOf,亦或者使用Object.prototype.toString.call方法进行运行识别。

浮点数

  • 来源:js浮点数标准采用的是二进制浮点数算术标准(IEEE 754)。所有浮点数的运算都是先转换成二进制再进行运算的,因此有可能出现运算溢出的现象。
  • 现象:0.1 + 0.2 !== 0.3。
  • 解决方案:将浮点数转换成整数,运算完后,将其转换回来。亦或者使用类似bignumber.js这样算数精度库去进行运算。

数组

  • 来源:由于js采用的继承拓展机制是原型链机制,因此js中万物皆从Object的拷贝开始。js中数组相比较其他语言的数组,可以说它不是真正的数组。只是一个类似于列表的高阶对象。它相比较对象而言只不过其构造器不同,以及其值按次序进行排列。
  • 现象:for of可以遍历数组值,也可以遍历对象值。
  • 方案:针对数组和对象的不同特性,思考其需要应对的场景,充分发挥数组和对象的优势。

小结

  • js本身由于其最初创造的时间和历史定位导致不可避免的会产生一些历史遗留问题,有一些甚至一度让js开发人员相当苦恼。但是在编程领域从来都是办法总比困难多的,因此一些常见历史问题基本都已经有了成熟的解决方案或者替代方案。但并不意味着我们可以只知道拿来用,而不知道其历史原因的存在,这是不符合程序员风范的。
  • 知道了js的另一面,但我们也不能忘记js那些优秀的特性。js能从当初粗糙走到今天的主流语言之一,这些优秀特性起到了一定的决定性作用。总结如下:
    • 函数:js早期设计吸收scheme的函数一等公民设计,将函数作为一等公民进行编程。这对于开发人员来说,是相当友好的。(详情可以查看,笔者不多叙述)
    • 原型链机制:js继承采用的是Self的原型链机制(详情点击,笔者不多叙述)
    • 字面量表示法:相比较其他语言,js将数据结构直接具象表示出来,可以通过一系列字面量去创建对象和数据结构。这也是json的来由之一。

最后

一切伟大的行动和思想,都有一个微不足道的开始