编写高质量的JavaScript代码(一)
欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~
2016年6月加入腾讯,目前在SNG社交网络质量部从事内部平台工具的研发。熟悉PHP、JS、CSS,喜欢弹吉他。
一、理解JavaScript的浮点数
由IEEE754标准制定,JavaScript中所有的数字都是双精度浮点数,即64位编码数字。
JavaScript大多数的算术运算符可以进行整数、浮点数或者两者的组合进行计算。但是位运算符比较特殊,JavaScript不会直接把操作数作为浮点数进行运算。需要这些步骤完成运算:
1、把操作数8和1转换成32位整数;
2、每一位按位或运算;
3、把结果转换成64位浮点数。比如:
8 | 1; //
//000000000000000000000000000001000 | 000000000000000000000000000001001 = 000000000000000000000000000001001
浮点数的计算是不精确的,浮点运算只能四舍五入到最接近的可表示的实数。当执行一系列的运算时,随着舍入误差的积累,运算结果会越来越不精确。比如:
0.1 + 0.2; //0.30000000000000004
0.1 + 0.2 + 0.3; //0.6000000000000001
加法中的结合律在JavaScript中对于浮点数有时候并不成立:
(0.1 + 0.2) + 0.3; //0.6000000000000001
0.1 + (0.2 + 0.3); //0.6
小心浮点数,解决其计算不精确的一个简单策略就是将浮点数转换成整数进行运算,整数的运算是精确的,不用担心舍入误差。
二、当心隐式的强制转换
JavaScript中,运算符+既重载了数字相加,又重载了字符串连接操作,这取决于其参数的类型,简单总结如下:
(1)如果两个操作数都是数值,执行常规加法运算
(2)如果有一个操作数是字符串,则将另一个操作数转换成字符串,再进行字符串的拼接
(3)如果有一个操作数是对象、数值或布尔值,如果 toString 方法存在并且返回原始类型,返回 toString 的结果。如果toString 方法不存在或者返回的不是原始类型,调用 valueOf 方法,如果 valueOf 方法存在,并且返回原始类型数据,返回 valueOf 的结果。其他情况,抛出错误。如果是undefined、null、NaN会调用String()函数取得字符串值’undefined’、’null’、’NaN’,再按照情形(2)进行运算
算数运算符-
、*
、/
、和%
在计算之前都会尝试将其参数转换为数字,简单总结如下:
(1)如果两个操作数都是数值,执行常规运算
(2)如果有一个数是NaN,则结果是NaN
(3)如果有一个操作数字符串、布尔值、null或undefined,则先调用Number()方法将其转换为数值,再进行运算
(4)如果有一个操作数是对象,如果 valueOf 存在,且返回原始类型数据,返回 valueOf 的结果。如果 toString 存在,且返回原始类型数据,返回 toString 的结果。其他情况,抛出错误。再按照上面规则进行运算。
因此,valueOf()
和toString()
方法应该被同时重写,并返回相同的数字字符串或数值表示,才不至于强制隐式转换时得到意想不到的结果。
逻辑运算符||
、&&
可以接受任何值作为参数,会将参数隐式的强制转换成布尔值。JavaScript中有6个假值:false、0、“”
、NaN、null和undefined,其他所有的值都为真值。因此在函数中判断参数是否是undefined不能简单的使用if,而应该使用typeof:
function isUndefined(a){
if (typeof a === 'undefined'){ //或者a === undefined
console.log('a is not defined')
}
}
三、避免对混合类型使用==
运算符
"1.0e0" == {valueOf: function(){return true}}; //true
相等操作符==在比较两个参数时会参照规则进行隐式转换,判断两个值是否相等,使用全等操作符===是最安全的。j简单总结一下==的隐式转换规则:
四、尽量少用全局对象,始终声明局部变量
定义全局变量会污染共享的公共命名空间,可能导致意外的命名冲突,不利于模块化,导致程序中独立组件间的不必要耦合。全局变量在浏览器中会被绑定到全局的window对象,添加或修改全局变量会自动更新全局对象,更新全局对象也会自动更新全局全局命名空间。
window.foo; //undefined
var foo = 'global foo';
window.foo; //"global foo"
window.foo = 'changed'
foo; //changed
JavaScript会把没有使用var声明的变量简单地当做全局变量,如果忘记声明局部变量,改变量会被隐式地转变成全局变量。任何时候都应该使用var声明局部变量。
function swap(array, i, j){
var temp = a[i]; //使用var声明局部变量,否则temp会变成全局变量
a[i] = a[j];
a[j] = temp;
}
五、理解变量提升
JavaScript不支持块级作用域,变量定义的作用域并不是离其最近的封闭语句或代码块,而是包含它们的函数。来看一个例子。
function test(params) {
for(var i = 0; i < 10; i++){
var params = i;
}
return params;
}
test(20); //
在for循环中声明了一个局部变量params,由于JavaScript不支持块级作用域,params重新声明了函数参数params,导致最后的结果并不是我们传进去的值。
理解JavaScript变量声明需要把声明变量看作由声明和赋值两部分组成。JavaScript隐式地提升声明部分到封闭函数的顶部,而将赋值留在原地。也就是变量的作用域是整个函数,在=语句出现的位置进行赋值。下面第一种方式会被JavaScript隐式地提升声明部分,等价于第二种方式那样。建议手动提升局部变量的声明,避免混淆。
function f() { function f() {
/*do something*/ var x;
//... //...
{ {
//... //...
var x = /*...*/ x = /*...*/
//... //...
} }
} }
JavaScript没有块级作用域的一个例外是异常处理,try-catch语句将捕获的异常绑定到一个变量,该变量的作用域只是catch语句块。下面的例子中catch语句块中的x值的改变并没有影响最初声明的x的值,说明该变量的作用域只是catch语句块。
function test(){
var x = 'var', result = [];
result.push(x);
try{
throw 'exception';
} catch(x){
x = 'catch';
}
result.push(x);
return result;
}
test(); //["var", "var"]
六、熟练掌握高阶函数
高阶函数是那些将函数作为参数或返回值的函数,是一种更为抽象的函数。函数作为参数(其实就是回调函数)在JavaScript中被大量使用:
[3,2,1,1,4,9].sort(function(){
if(x < y){
return -1;
}
if(x > y){
return 1;
}
return 0;
}); //[1,1,2,3,4,9] var name = ['tongyang', 'Bob', 'Alice'];
name.map(function(name){
return name.toUpperCase();
}); //['TONGYANG', 'BOB', 'ALICE']
学会使用高阶函数通常可以简化代码并消除繁琐的样板代码,如果出现重复或者相似的代码,我们可以考虑使用高阶函数。
var aIndex = "a".charCodeAt(0); //
var alphabet = "";
for(var i = 0; i < 26; ++i){
alphabet += String.fromCharCode(aIndex + i)
}
alphabet; //"abcdefghijklmnopqrstuvwxyz" var digits = "";
for(var i = 0; i < 10; ++i){
digits += i;
}
digits; // var random = "";
for(var i = 0; i < 8; ++i){
random += String.fromCharCode(Math.floor(Math.random() * 26) + aIndex);
}
random; //atzuvtcz
这三段代码有相同的基本逻辑,按照特定的规则拼接字符串。我们使用高阶函数来重写这段代码
function buildString(number, callback){
var result = "";
for(var i = 0; i < number; ++i){
result += callback(i);
}
return result;
} var aIndex = "a".charCodeAt(0); //
var alphabet = buildString(26, function(i){
return String.fromCharCode(aIndex + i);
});
var digits = buildString(10, function(i){
return i;
});
var random = buildString(8, function(){
return String.fromCharCode(Math.floor(Math.random() * 26) + aIndex);
});
相比之下,高阶函数更简捷,逻辑更清晰,掌握高阶函数会提高代码质量,这需要多读优秀的源码,多在项目中实践才能熟练的掌握。
七、在类数组对象上复用通用的数组方法
Array.prototype中的标准方法被设计成其他对象可复用的方法,即使这些对象没有继承Array。
在JavaScript中很常见的类数组对象是DOM中的NodeList。类似document.getElementsByTagName这样的操作会查询Web页面中的节点,并返回NodeList作为搜索的结果。我们可以在NodeLIst对象上面使用通用的数组方法,比如forEach、map、filter。
scriptNodeList = document.getElementsByTagName('script');
[].forEach.call(scriptNodeList, function(node){
console.log(node.src);
});
类数组对象有两个基本特征:
(1)具有一个整形length属性
(2)length属性大于该对象的最大索引。索引是一个整数,它的字符串表示的是该对象中的一个key
可以用一个对象字面量来创建类数组对象:
var arrayLike = {0: "a", 1: "b", 2: "c", length: 3};
var result = [].map.call(arrayLike, function(s){
return s.toUpperCase();
});
result; //["A", "B", "C"]
字符串也可以使用通用的数组方法
var result = [].map.call("abc", function(s){
return s.toUpperCase();
}); //["A", "B", "C"]
只有一个Array方法不是通用的,即数组连接方法concat。这个方法会检查参数的[[Class]]属性。如果参数是一个真实的数组,则会将该数组的内容连接起来作为结果;否则,参数将以一个单一的元素来连接.
function namesColumn() {
return ["Names"].concat(arguments);
}
namesColumn('tongyang', 'Bob', 'Frank'); //["Names", Arguments[3]]
可以使用slice方法来达到我们的目的
function namesColumn() {
return ['Names'].concat([].slice.call(arguments));
}
namesColumn('tongyang', 'Bob', 'Frank'); /*["Names", "tongyang", "Bob", "Frank]*/
在类数组对象上复用通用的数组方法可以极大的减少冗余代码,提高代码质量
欢迎加入QQ群:374933367,与腾云阁原创作者们一起交流,更有机会参与技术大咖的在线分享!
相关阅读
Kotlin Native 详细体验,你想要的都在这儿
Java 程序员快速上手 Kotlin 11招
JavaScriptCore全面解析 (上篇)
此文已由作者授权腾讯云技术社区发布,转载请注明文章出处
原文链接:https://www.qcloud.com/community/article/560964
获取更多腾讯海量技术实践干货,欢迎大家前往腾讯云技术社区
编写高质量的JavaScript代码(一)的更多相关文章
- 如何编写高质量的Javascript代码
1.避免全局变量,因为全局变量容易发生名称上的冲突,可维护性不好. a,使用命名空间 b,使用闭包 c,在函数内部使用var声明 2.编写可维护的代码 a.可读性 b.连续性 c.预见性 d.看起来是 ...
- 高质量的javascript代码 -- 深入理解Javascript
一. 编写高质量的javascript代码基本要点a) 可维护的代码(Writing Maintainable Code)i. 可读(注释)ii. 一致(看上去是同一个人写的)iii. 已记录b) 最 ...
- HTML Inspector – 帮助你编写高质量的 HTML 代码
HTML Inspector 是一款代码质量检测工具,帮助你编写更优秀的 HTML 代码.HTML Inspector 使用 JavaScript 编写,运行在浏览器中,是最好的 HTML 代码检测工 ...
- iOS应用开发最佳实践系列一:编写高质量的Objective-C代码
本文由海水的味道编译整理,转载请注明译者和出处,请勿用于商业用途! 点标记语法 属性和幂等方法(多次调用和一次调用返回的结果相同)使用点标记语法访问,其他的情况使用方括号标记语法. 良好的 ...
- 编程精粹--编写高质量C语言代码(3):自己设计并使用断言(二)
接着上一遍文章<<编程精粹--编写高质量C语言代码(2):自己设计并使用断言(一)>>,继续学习怎样自己设计并使用断言,来更加easy,更加不费力地自己主动寻找出程序中的错误. ...
- 如何写出高质量的JavaScript代码
优秀的Stoyan Stefanov在他的新书中(<Javascript Patterns>)介绍了很多编写高质量代码的技巧,比如避免使用全局变量,使用单一的var关键字,循环式预存长度等 ...
- 如何编写高质量的js代码--底层原理
转自: 如何编写高质量的 JS 函数(1) -- 敲山震虎篇 本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/7lCK9cHmunvYlbm ...
- 怎样编写高质量的java代码
代码质量概述 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友 ...
- 怎样编写高质量的 Java 代码
代码质量概述 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友提出宝贵 ...
随机推荐
- <abbr>标签的
表示一个缩写形式,比如 "Inc."."etc.".通过对缩写词语进行标记,您就能够为浏览器.拼写检查程序.翻译系统以及搜索引擎分度器提供有用的信息. 将一个标 ...
- 设置border属性变化不同形状:三角形、圆形、弧形 2017-03-20
一.通过设置边框----正方形.三角形 <style> .c{ height: 0px; width: 0px; border-top: 50px solid red; border-ri ...
- 判断是否支持WebP
PC端,触屏版: 前端JS方案——利用img标签加载一张base64的WebP图片,在img标签的onload事件中判断该图片是否具有宽高的属性,若有表示支持webP,若没有表示不支持webP.后台判 ...
- impress.js初体验
概述 如果你已经厌烦了使用PowerPoint制作PPT,那么impress.js是一个非常好的选择,用它做的PPT更加直观,效果也非常的不错.装X是需要一定代价的,不过如果你是个前端爱好者那么一切就 ...
- 我的python之路【第二篇】数据类型与方法
一.Python中有哪些数据类型 整型 在32位的系统中: 取值范围就是-(2^31) 到2^31-1 在64位系统中: 取值范围就是-(2^63) 到2^63-1 浮点型 布尔型 字符型 字符串 ...
- web之Respone
服务器处理请求的流程: 服务器每次收到请求时,都会为这个请求开辟一个新的线程. 服务器会把客户端的请求数据封装到request对象中,request就是请求数据的载体!(袋子) 服务器还会创建r ...
- PRINCE2 有级别吗?
PRINCE2是分级别的,有基础级和专业级两个级别 Foundation基础级考试没有报考条件限制, 完全根据学员掌握知识的能力和实际工作经验 Practitioner 专业级考试不可越级 1.持有P ...
- 从PRINCE2引起项目失败的共性原因?
一.项目与组织的关键战略优先排序之间缺乏明确的联系 项目必须体现和贯彻发起者的目标.每个项目是怎样支持这些目标的,怎样对项目进行优先排序能提供最大的回报,这些都应该能明确地表述出来. PRINCE2持 ...
- 树莓派Raspberry实践笔记-简单方法安装minicom
[原创链接]:http://www.cnblogs.com/atsats/p/6665566.html 本文结合最新的Raspbain jessie,使用图形化的方式安装一款软件:串口调试工具mini ...
- Spring框架下的单元测试
一.使用spring中对Junit框架的整合功能 除了junit4和spring的jar包,还需要spring-test.jar.引入如下依赖: <dependency> <grou ...