javascript优化项目代码技巧之语言基础

PG30吕润军的博客      2022-02-13     118

关键词:

1.全局变量污染与变量提升
2.数据类型
3.特殊值(NaN、undefined、null)
4. === 与 ==
5.没有真正的数组
6.避免使用with与eval
7.消除switch歧义
8.不要省略块标志 { }

    Javascript的弱类型以及函数作用域等规则使用编写Javascript代码极为容易,但是编写可维护、高质量的代码却变得十分困难,这个系列的文章将总结在项目开发过程中,能够改善代码可读性、可维护性及优化运行性能的一系列技巧。
    如有问题,请不吝指出,非常感谢;如果喜欢,右下角点个推荐吧~

1.全局变量污染与变量提升

  • 定义全局变量的3种方式
1
2
3
var key = 'value'; // 所有函数外执行
window.key = 'value'; // window为全局对象
key = 'value'; // 省去 var,隐式的全局变量
  • 全局对象是所有域中都可见的变量,如果程序比较小,那么定义全局变量可以避免函数调用时参数的传递,但是对于一个大的项目,全局变量定义不善,极为容易造成全局变量被某个程序改掉,而没有被发现,这也使调试时难以发现问题所在。
  • 在项目较大时,建议尽可能地避免使用全局变量,那么兼顾灵活性与可读性的解决方法就是在程序中只创建一个全局变量。
1
2
3
4
5
6
7
8
9
10
/**
 * 只有Person一个全局变量,
 * 其余变量定义在Person下,尽可能地避免与其他变量名冲突
 */
var Person = { name:"A", age:20 };
Person.parents = {
    father:"A",
    mother:"C"
}
Person.getAge = function() { /* ... */ }
  • 与C、Python等语言拥有块级作用域不同,Javascript作用域是以函数为单位的,定义在函数中的变量函数外部不可见,但是在函数内部(包括其子函数)处处可见。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * 考虑下面的函数,先运行f1(),再var b = 4,
 * 但是事实上,f1()中的b就是之后定义的b
 * 这是变量提升特性造成的,就是说所有变量定义都将提到函数最前面
 * 局部变量的优先级高于外部变量,因此f()外部的b至始至终没有变过
 */
var b = 15;
var f = function() {
    var a = 100;
    var f1 = function(){
        var c = 3;
        b = a + c;  // => b=103 变量提升,这里的b是函数f()内部的b
    };
    f1();
    var b = 4;
    console.log(a , b , c); // => a=100,b=4,c=undefined
}
f(); 
console.log(b); // => b=15,b并没有发生改变

2.数据类型

  • 浮点数是不精确的,在项目中,特别是在线支付环节,如果希望计算的结果是准确的,一般先乘100转换为整数后,计算完后再除以100,这样能够得到预想中的结果。
1
0.1 + 0.2 === 0.3 // => false
  • 检测数据类型,我们先看面试中常出现的判断题
1
2
3
4
5
6
7
typeof null === 'null' // => false
typeof null === 'object' // => true
typeof NaN === 'NaN'  // => false
typeof NaN === 'number' // => true
// javascript6个基本类型如下,typeof总是返回这6个值
// number、string、boolean、object、function、undefined
// chrome 50中,typeof 3/0 结果为 NaN,有博友知道为什么求告知
  • typeof null 返回的是'object',项目开发中,我们并不希望null被判断为'object',自定义一个函数,可以满足需求。
1
2
3
function type(ob){
    return (ob === null ) ? "null" : (typeof ob);
}
  • 上面的方法不能识别数组、日期等对象,如果需要判断日期、数组、正则、错误等类型,那么可以考虑使用toString()方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 更多内置类型,例如 Math 等可以根据需要添加
function type(ob){
    var _toString = Object.prototype.toString;
    var _type = {
        "undefined" : "undefined",
        "number" : "number",
        "boolean" : "boolean",
        "string" : "string",
        "[object Function]" : "function",
        "[object RegExp]" : "regexp",
        "[object Array]" : "array",
        "[object Date]" : "date",
        "[object Error]" : "error"
    }
    return _type[typeof ob] || _type[_toString.call(ob)] || (ob ? "object" : "null");
}
  • 自定义类型,可结合typeof、constructor属性和instanceof实现

3.特殊值(NaN、undefined、null)

1
2
3
4
5
typeof NaN === 'number' // => true 类型为number,表示非数字
'0.1223' // => 0.1223
'1e6' // => 1000000
'1a3' // => NaN
1.2 + NaN // => NaN
  • Javascript提供了isNaN函数检测NaN值
1
2
3
4
5
6
7
8
9
10
11
// 不能转换为数字的值,均返回true,
// 因此并不能用这个函数检测一个值是否真的为 NaN
isNaN(NaN) // => true
isNaN('abc') // => true
isNaN(0)  // => false
isNaN('1e6') // => false
isNaN(3/0) // => fasle
 
// NaN是一个唯一自己与自己不相等的值
NaN === NaN // false
NaN !== NaN // true
  • 如何检查一个值是不是真的NaN
1
2
3
4
5
6
7
8
// 方法一
function _isNaN(value){
    return value !== value;
}
// 方法二
function _isNaN(value) {
    return typeof value === 'number' && isNaN(value);
}
  • 使用isFinite检查是否可以是个数或可被转换为一个数
1
2
3
4
isNaN(3/0) // => fasle,虽然3/0 不能代表一个数,但是isNaN不能识别
isFinite(3/0) // => false,isFinite能检测 NaN和正负无穷大
isFinite("234") // => true,isFinite 可以将参数转换为数字
isFinite(true) // => true,可转化为数字的字符串和布尔值返回true
  • 判断参数是否真的是一个数字,而不是可转换为数字的字符串等
1
2
3
4
// 加一个基本类型判断就OK
function isNumber (value) {
    return typeof value === "number" && isFinite(value);
}
  • undefined有如下几种情况

    (1) 定义了变量但没有赋值 var cc; cc === undefined // => true (2) 获取一个对象不存在的属性 (3) 没有返回值的函数,new + 构造函数除外

    (4) 定义函数时声明多个形参,调用时传入参数个数少于声明,多余的参数为undefined

  • null 只能显示指定,一般用来清空对象
  • 0、NaN、''(空字符串)、false、null、undefined的布尔值为false(注意,[](空数组),{}(空对象)布尔值为 true )

1
2
3
var a = 4 ;
if({})a++; // => a=5 空对象可通过 Object.keys().length判断
if([])a++; // => a=6 空数组通过 length 判断

4. === 与 ==

  • 使用 == 比较前如果不是相同类型的值,将进行类型转换,转换规则请看下面的例子。
1
2
3
4
5
6
7
8
'' == 0  // => true,String与Number,将String转为 Number
0 == '0' // => true,同上
'' == '0'// => false,2个都是字符串,因此直接比较
fasle == '0'  // => true,Boolean与其他,将Boolean转为Number
true == '3'  // => false,同上
false == undefined // => false,同上,0与undefined不等
false == 'false'   // => false,同上
null == undefined // => true,null与undefined比较返回true
  • === ,如果类型不同,则直接返回false,类型相同再进行比较。
  • == 与 != 将进行隐式的类型转换,转换规则复杂,因此在实际的项目开发中,建议避免使用 == 与 !=,尽量使用 === 与 !==。

5.没有真正的数组

  • 在Javascript中,数组也是对象,因此typeof的结果是object,如果需要判断一个对象是不是数组,那么可以使用第2部分的toString()方法,当然还可以利用构造函数判断。
1
typeof value === 'object' && value.constructor === Array
  • 需要注意的是,函数参数arguments并不是数组,只是一个具有length属性,以数字作为键的对象,因此arguments不能使用数组的方法,但是利用上述方法仍将arguments判断为数组。
  • 有时我们需要对arguments对象执行数组的某些方法,这个时候可以利用下面的方法将arguments转换为真数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* 将arguments对象转换为数组 */
var args = Array.prototype.slice.call(arguments);
/* 具有length属性,键为数字且从0开始的对象 */
a = {0:"c",1:"d",length:2 }
Array.prototype.slice.call(a); // => ["c","d"]
/* 没有length属性 */
a = {0:"c",1:"d"}
Array.prototype.slice.call(a); // => [ ] 空数组
/* 猜测slice方法实现方式 */
function slice(ob){
    var arr = new Array();
    for(var i = 0; i < ob.length; i++ ){
        arr[i] == ob[i];
    }
    return arr;
}

6.避免使用with与eval

1
2
3
4
5
6
7
var ob = { a: 1, b: 2};
var d = 0;
with(ob){
    a = 3;  // ob.a = 3
    c = 4;  // ob.c = undefined,window.c = 4,无心声明了全局变量
    d = 5;  // ob.d = undefined, d为with外申明的变量
}
  • 不推荐使用with的3个理由

    (1) 使用with时,先检查对象是否有该属性,存在则使用,不存在则继续查找作用域,会降低执行性能
    (2) with语句中的变量语意不明确,可读性差

    (3) 例如上例中的c不小心隐式声明为全局变量,本意是给ob.c赋值,易出现问题且难以调试

  • 对于eval(),可以直接执行一段js代码,不推荐理由如下

    (1) eval需要将字符串解释为代码后执行,降低执行性能
    (2) eval降低程序的可读性,例如 eval("var a = ob." + key)等价于var a=ob[key],后者明显更优

    (3) eval存在安全问题,任意传入字符串都可能被解析执行,恶意代码也不例外

7.消除switch歧义

  • switch中的case语句,定义了函数的起点,却没有定义函数的终点,不要忘记case语句后使用break
  • 有时候,我们确实需要贯穿case语句,比如多种case情况,执行同一个方法,这时候,建议显式声明,消除歧义
1
2
3
4
5
6
7
8
9
10
11
12
// 在需要贯穿的case语句后添加 /* empty */
// 避免检查代码时被认为忘记写了执行语句
// 显示申明,语意也更明确,提高可读性
switch(a){
    case 1: /* empty */
    case 2:
        func1();
        break;
    case 3:
        func2();
        break;
}

8.不要省略块标志 { }

  • if、while、do、for等语句接受{ ... } 代码块,但是有时我们在代码块中只有一行代码,为了使代码看起来简洁,很可能省略 { },但是这可能产生问题。
1
2
3
4
5
6
7
8
9
10
11
12
if(a)
    if(b)
        func1();
else
    func2();
// => 等价于
if(a)
    if(b)
        func1();
    else
        func2();
// 这并不是想要的结果,js不是python,还是加上大括号比较好
  • 加上括号,也增强了代码的可读性,下面这种写法是不是一目了然了呢

 

1
2
3
4
5
6
7
8
9
// 很清晰地看出 if(a) 和 else 并列
// if(b) 是 if(a) 的子语句
if(a){
    if(b){
        func1();
    }
}else{
    func2();
}

dl:深度学习模型优化之模型训练技巧总结之适时自动调整学习率实现代码(代码片段)

DL:深度学习模型优化之模型训练技巧总结之适时自动调整学习率实现代码目录深度学习模型优化之模型训练技巧总结之适时自动调整学习率实现代码深度学习模型优化之模型训练技巧总结之适时自动调整学习率实现代码defsc... 查看详情

优雅编程|7个你应该掌握的javascript编码技巧

JavaScript是一个灵活性很强的语言,有很多和其他语言不一样的特性,本文分享7个日常开发中可以用到的JavaScript编码技巧,享受其优雅编程的快感。更多的编码技巧可以参阅《18个JavaScript代码的小技巧》和《24个Javascript代码优化... 查看详情

15个javascript优化技巧

一段精简的代码不仅能让人心情愉悦,而且也会让代码的逼格有所提升。一个优秀的产品往往需要一点一点的打磨才能脱颖而出,精简的代码是其中重要的组成部分。那么,就让我们来了解一下一些常见的优化代码的手段吧。1... 查看详情

python代码性能优化技巧

...是在performance较差的机器上,因此有必要进行一定的代码优化来提高程序的执行效率。如何进行Python性能优化,是本文探讨的主要问题。本文会涉及常见的代码优化方法,性能优化工具的使用以及如何诊断代码的性能瓶颈等内容... 查看详情

sql优化之sql进阶技巧(上)(代码片段)

...要目录如下: SQL的书写规范 SQL的一些进阶使用技巧 SQL的优化方法SQL的书写规范在介绍一些技巧之前,有必要强调一下规范,这一点我发现工作中经常被人忽略,其实遵循好的规范可读性会好很多,应该遵循哪些规范呢1、表名... 查看详情

编写高效的java代码:常用的优化技巧之并发编程技巧

​​编写高效的Java代码:常用的优化技巧【一】​​​​编写高效的Java代码:常用的优化技巧【二】​​​​编写高效的Java代码:常用的优化技巧【三】之JVM调优​​一、使用并发集合和原子变量来减少竞争在多线程并发执行... 查看详情

编写高效的java代码:常用的优化技巧之并发编程技巧

​​编写高效的Java代码:常用的优化技巧【一】​​​​编写高效的Java代码:常用的优化技巧【二】​​​​编写高效的Java代码:常用的优化技巧【三】之JVM调优​​一、使用并发集合和原子变量来减少竞争在多线程并发执行... 查看详情

getmysql这5个优化技巧(代码片段)

...篇文章主要谈谈MySQL数据库在发展周期中所面临的问题及优化方案,暂且抛开前端应用不说,大致分为以下五个阶段:阶段一:数据库表设计项目立项后,开发部门根据产品部门需求开发项目。开发工程师在开发项目初期会对表... 查看详情

howjavascriptworks(javascript工作原理)渲染引擎及性能优化小技巧(代码片段)

...     |----> RenderTreeCSSOMtree ----| 这是JavaScript工作原理的第十一章。迄今为止,之前的JavaScript工作原理系列文章集中于关注JavaScri 查看详情

sql优化之sql进阶技巧(上)(代码片段)

...要目录如下: SQL的书写规范 SQL的一些进阶使用技巧 SQL的优化方法SQL的书写规范在介绍一些技巧之前,有必要强调一下规范,这一点我发现工作中经常被人忽略,其实遵循好的规范可读性会好很多,应该遵循哪些规范呢1、表名... 查看详情

hive数仓项目之基本优化方案学生出勤主题看板分析(代码片段)

...题看板增量的流程Hive数仓项目之需求分析、建模分析、优化方案Hive数仓项目之sqoop相关操作、访问咨询主题看板_全量流程今日内容:1-相关的优化点:(整理归纳到笔记中)基 查看详情

18个你需要知道的javascript优化技巧(代码片段)

本文来源于个人多年的JavaScript编码技术经验,适合所有正在使用JavaScript编程的开发人员阅读。本文的目的在于帮助大家更加熟练的运用JavaScript语言来进行开发工作。1.多个条件的判断当我们需要进行多个值的判断时,我... 查看详情

项目中常用的19条mysql优化技巧(代码片段)

...文:https://segmentfault.com/a/1190000012155267声明一下:下面的优化方案都是基于“Mysql-索引-BTree类型 ”的一、EXPLAIN做MySQL优化,我们要善用 EXPLAIN 查看SQL执行计划。下面来个简单的示例,标注(1,2,3,4,5)我们要重点关注... 查看详情

高性能的javascript代码,优化技巧分享

JavaScript作为当前最为常见的直译式脚本语言,已经广泛应用于Web应用开发中。为了提高Web应用的性能,从JavaScript的性能优化方向入手,会是一个很好的选择。本文从加载、上下文、解析、编译、执行和捆绑等多个方... 查看详情

js代码优化小技巧

...;   fn=callback||function();  script.type=‘text/javascript‘; &nbs 查看详情

ios之性能优化·优化app界面渲染与保持界面流畅性的技巧(代码片段)

一、界面渲染流程①渲染流程分析计算机中的显示过程通常是通过CPU、GPU、显示器协同工作来将图片显示到屏幕上,如下图所示:苹果为了解决图片撕裂的问题使用了VSync+双缓冲区的形式,就是显示器显示完成一... 查看详情

hive数仓项目之需求分析建模分析优化方案(代码片段)

 往期内容:Hive数仓项目架构说明、环境搭建及数据仓库基础知识Hive数仓项目之数仓分层、数仓工具的使用Hive数仓项目之访问咨询主题看板:数据的采集、转换、分析导出Hive数仓项目之访问咨询主题看板增量的流程​... 查看详情

编写高效的java代码:常用的优化技巧

​​编写高效的Java代码:常用的优化技巧【二】​​​​编写高效的Java代码:常用的优化技巧【三】之JVM调优​​​​编写高效的Java代码:常用的优化技巧【四】之并发编程技巧​​1.代码结构优化代码结构优化是提高Java程序... 查看详情