编写高质量的JavaScript代码(一)

时间:2022-09-23 15:20:18

欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~

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简单总结一下==的隐式转换规则:

编写高质量的JavaScript代码(一)

四、尽量少用全局对象,始终声明局部变量

定义全局变量会污染共享的公共命名空间,可能导致意外的命名冲突,不利于模块化,导致程序中独立组件间的不必要耦合。全局变量在浏览器中会被绑定到全局的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代码(一)的更多相关文章

  1. 如何编写高质量的Javascript代码

    1.避免全局变量,因为全局变量容易发生名称上的冲突,可维护性不好. a,使用命名空间 b,使用闭包 c,在函数内部使用var声明 2.编写可维护的代码 a.可读性 b.连续性 c.预见性 d.看起来是 ...

  2. 高质量的javascript代码 -- 深入理解Javascript

    一. 编写高质量的javascript代码基本要点a) 可维护的代码(Writing Maintainable Code)i. 可读(注释)ii. 一致(看上去是同一个人写的)iii. 已记录b) 最 ...

  3. HTML Inspector – 帮助你编写高质量的 HTML 代码

    HTML Inspector 是一款代码质量检测工具,帮助你编写更优秀的 HTML 代码.HTML Inspector 使用 JavaScript 编写,运行在浏览器中,是最好的 HTML 代码检测工 ...

  4. iOS应用开发最佳实践系列一:编写高质量的Objective-C代码

          本文由海水的味道编译整理,转载请注明译者和出处,请勿用于商业用途! 点标记语法 属性和幂等方法(多次调用和一次调用返回的结果相同)使用点标记语法访问,其他的情况使用方括号标记语法. 良好的 ...

  5. 编程精粹--编写高质量C语言代码(3):自己设计并使用断言(二)

    接着上一遍文章<<编程精粹--编写高质量C语言代码(2):自己设计并使用断言(一)>>,继续学习怎样自己设计并使用断言,来更加easy,更加不费力地自己主动寻找出程序中的错误. ...

  6. 如何写出高质量的JavaScript代码

    优秀的Stoyan Stefanov在他的新书中(<Javascript Patterns>)介绍了很多编写高质量代码的技巧,比如避免使用全局变量,使用单一的var关键字,循环式预存长度等 ...

  7. 如何编写高质量的js代码--底层原理

    转自: 如何编写高质量的 JS 函数(1) -- 敲山震虎篇   本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/7lCK9cHmunvYlbm ...

  8. 怎样编写高质量的java代码

    代码质量概述     怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友 ...

  9. 怎样编写高质量的 Java 代码

    代码质量概述 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友提出宝贵 ...

随机推荐

  1. 2016 - 3 - 12 SQLite的学习之SQL语言入门

    1.SQL语句的特点: 1.1 不区分大小写 1.2 每条语句以;结尾 2.SQL语句中常用关键字: select,insert,update,from,create,where,desc,order ...

  2. Eclipse:Dubug快捷键

    Debug快捷键 F5单步调试进入函数内部.   F6单步调试不进入函数内部,如果装了金山词霸2006则要把"取词开关"的快捷键改成其他的.   F7由函数内部返回到调用处.   ...

  3. jQuery源码解读-事件分析

    最原始的事件注册 addEventListener方法大家应该都很熟悉,它是Html元素注册事件最原始的方法.先看下addEventListener方法签名: element.addEventList ...

  4. CountdownLatchTest

    import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java ...

  5. SQL Server中的三种物理连接操作

    来源:https://msdn.microsoft.com/zh-cn/library/dn144699.aspx 简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Out ...

  6. Telnet命令检测远程主机上的端口是否开启

    ping命令不能检测端口,只能检测你和相应IP是否能连通. 本地虚拟机里安装了一个Ubuntu,使用Putty连接22端口操作时提示失败,于是查看对应端口是否开启. Windows下要检测远程主机上的 ...

  7. c&plus;&plus;构造函数详解

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  8. Mysql 多表联合查询效率分析及优化

    1. 多表连接类型 1. 笛卡尔积(交叉连接) 在MySQL中可以为CROSS JOIN或者省略CROSS即JOIN,或者使用','  如: SELECT * FROM table1 CROSS JO ...

  9. js设备判断

    判断设备android,weixin,ios var UserAgent=navigator.userAgent.toLowerCase(); var IS_ANDROID=false; var IS ...

  10. Android URI简单介绍

    就Android平台而言,URI主要分三个部分:scheme, authority and path.当中authority又分为host和port.格式例如以下: scheme://host:por ...