Javascript 优化项目代码技巧之语言基础(一)

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();

}

时间: 2024-10-27 16:31:49

Javascript 优化项目代码技巧之语言基础(一)的相关文章

优化PHP代码技巧的小结

优化PHP代码技巧的小结1. 如果一个方法能被静态,那就声明他为静态的,速度可提高 1/4;2. echo 的效率高于 print,因为 echo 没有返回值,print 返回一个整型;3. 在循环之前设置循环的最大次数,而非在在循环中;4. 销毁变量去释放内存,特别是大的数组;5. 避免使用像__get, __set, __autoload 等魔术方法;6. requiere_once()比较耗资源;7. 在 includes 和 requires 中使用绝对路径,这样在分析路径花的时间更少;

C#语言基础——7月21日

C#语言基础 一.语言基础 (一).函数的四要素:      名称,输入,输出,加工(二).主函数.输出语句.输入语句:     Static void Main(string[] args)//下划线部分可以自己指定 { 主函数的内容 } Console.WriteLine();//输出语句,自动换行 Console.Write();//输出语句,不自动换行 Console.ReadLine();//输入语句 (三).注意: 1.大小写敏感 2.所有的符号都要用英文 3.不要漏掉 “;” (四

优化HTML代码的多种技巧

如何提升Web页面的性能,很多开发人员从多个方面来下手如JavaScript.图像优化.服务器配置,文件压缩或是调整CSS. 很显然HTML 已经达到了一个瓶颈,尽管它是开发Web 界面必备的核心语言.HTML页面的负载也是越来越重.大多数页面平均需要40K的空间,像一些大型网站会包含数以千计的HTML 元素,页面Size会更大. 如何有效的降低HTML 代码的复杂度和页面元素的数量,本文主要解决了这个问题,从多个方面介绍了如何编写简练,清晰的HTML 代码,能够使得页面加载更为迅速,且能在多种

JavaScript基础---语言基础(4)

函数,对象和数组 学习要点: 1.函数声明 2.return返回值 3.arguments对象 4.Object类型 5.Array类型 6.对象中的方法 函数是定义一次但却可以调用或执行任意多次的一段JS代码.函数有时会有参数,即函数被调用时指定了值的局部变量.函数常常使用这些参数来计算一个返回值,这个值也成为函数调用表达式的值. 一.函数声明 函数对任何语言来说都是一个核心的概念.通过函数可以封装任意多条语句,而且可以在任何地方.任何时候调用执行.ECMAScript中的函数使用functi

前端网络、JavaScript优化以及开发小技巧

一.网络优化 YSlow有23条规则,中文可以参考这里.这几十条规则最主要是在做消除或减少不必要的网络延迟,将需要传输的数据压缩至最少. 1)合并压缩CSS.JavaScript.图片,静态资源CDN缓存 通过构建工具Gulp,可以在开发的时候就将合并压缩的事情一起做掉. 之所以要做合并压缩是因为:HTTP 1.x不允许一个连接上的多个响应数据交错到达(多路复用),因而一个响应必须完全返回后,下一个响应才会开始传输. 也就是说即使客户端同时发送了两个请求,而且CSS资源先准备就绪,服务器也会先发

JavaScript基础---语言基础(3)

流程控制语句 学习要点: 1.switch语句 2.for...in语句 3.break和continue语句 4.with语句 ECMA-262规定了一组流程控制语句.语句定义了ECMAScript中的主要语法,语句通常由一个或者多个关键字来完成给定的任务.诸如:判断.循环.退出等.   一.switch语句 switch语句是多重条件判断,用于多个值相等的比较. var box = 1; switch (box) {                                      

Web性能优化系列:10个JavaScript性能提升的技巧

由 伯乐在线 - Delostik 翻译,黄利民 校稿.未经许可,禁止转载!英文出处:jonraasch.com.欢迎加入翻译小组. Nicholas Zakas是一位 JS 大师,Yahoo! 首页的前端主程.他是<高性能 Javascript>的作者,这本书值得每个程序员去阅读. 当谈到 JS 性能的时候,Zakas差不多就是你要找的,2010年六月他在Google Tech Talk发表了名为<Speed Up Your Javascript>的演讲. 但 Javascrip

web项目代码性能优化

这是我第一次使用博客园,以后它将陪伴我IT工作生活的一部分,以后每次有工作体会和成长我都会用它记录下来. 今天的第一篇文章是我测试的好朋友发给我的<淘宝前台系统性能分析与优化>,看完后,我结合自己开发经历整理了几个简单的编码习惯的改变,这样有助于你程序和系统系能的有限提升. 只有从小事做起,例如从注重以下几个编码习惯,虽然只能有限的提升性能,但从细节入手之后才能有更大的提升,最后才可能达到从架构和服务器方面的优化来提高系能. 以下优化措施都是非必须的,根据具体情况采取相应优化措施即可. 一.服

javascript--浏览器对象模型BOM、文本对象模型DOM、JavaScript 语言基础ECMAScript

JavaScript 的内容,包含以下三部分: ECMAScript(核心):JavaScript 语言基础: DOM(文档对象模型):规定了访问HTML和XML的接口: BOM(浏览器对象模型):提供了独立于内容而在浏览器窗口之间进行交互的对象和方法. 一. ECMAScript ECMAScript 规定了 JavaScript 脚本的核心语法,如 数据类型.关键字.保留字.运算符.对象和语句等,它不属于任何浏览器. ECMAScript 标准定义了 JavaScript 脚本中最为核心的内