本章主要帮助大家写出高质量的JS代码的方法,模式和习惯,例如:避免使用全局变量,使用单个的var变量声明,缓存for循环的长度变量length等

一、尽量避免使用全局变量

  1 每一个js环境都有一个全局对象,通过this可以访问,创建的每一个全局变量都归这个全局对象所有,在浏览器中,这个全局对象this等于window(在其他环境中,this对象不一定是window对象) 

var sss="sss";
this.sss;//"sss"
window.sss;//"sss"

  1.1全局变量导致的问题

    1,与第三方JS库发生命名冲突;2,与广告合作伙伴的脚本发生命名冲突;3,与来自第三方的统计脚本或者分析脚本发生命名冲突;4,代码移植,如果你的代码换个环境执行,可能与另外一个环境的代码相冲突

    2, 全局变量总是出现的原因:1,JS有暗示全局变量的概念,即任何变量,如果没有声明过,那么就是全局变量;2,隐式的创建了全局变量反模式--使用var声明的链式赋值

var a=b=0;
//这一切的原因是操作符的优先级,=操作符的优先级是从右向左,所以上面的例子,实际上相当于
//var a=(b=0);因此相当于隐式地创建了全局变量b,
/*正确的写法:
*var a,b,c;
*a=b=c=0;
*/

  1.2 变量释放(即delete删除变量)时的副作用

    1,隐含的全局变量(即不声明而直接使用的变量)与明确定义的全局变量(即使用var定义的全局变量)的不同之处在于,能否使用delete操作符删除该变量

      首先我们理解一下delete操作符,delete操作符是用来删除对象的属性的,使用var定义的全局变量,不能被删除,而隐含的全局变量是可以删除的,因此隐含的全局变量其实并不是真正意义上的变量,而是作为全局对象的属性存在的

var m="111";
delete m;//false
console.log(m);//
fff="d";
delete fff;//true
console.log(fff);//Uncaught ReferenceError: fff is not defined

  1.3 访问全局对象

    按照以下方式获取全局对象,因为函数的调用(这里不包括使用new操作符执行的函数)一般都指向全局对象    

    但是在ECMAScript5的严格模式下不能如此使用,严格模式下对全局对象的访问:将库代码打包到一个直接函数,然后传递一个引用给this,即把this看成传递到直接函数的一个参数。

var global=(function(){
  return this;
})();

  1.4 单一var模式:只使用一个var,在函数的顶部对该函数中所有的变量进行声明

  优点:

    1、在一个位置可以查找到函数所需要的所有局部变量;

    2、防止变量未声明就使用;

    3、更少编码;

    4、声明的时候进行初始化,防止在后期使用时出现逻辑错误(数字变量当成字符串这样低级的错误)

    5、将DOM引用赋值给局部变量,不需要每次都去重新进行DOM搜索,可大量节约时间

function fun(){
var a=1,
     b=2,
     my={},
    i,
    j;
//函数体
}
function fun(){
var element=document.getElementById("result"),
   style=element.style;
}

  1.5、提升:无论在js函数内的任意位置声明的变量,效果都等同于在函数顶部进行声明

    JS允许在函数中的任意位置声明变量,但无论在哪里声明最终都会被提升到函数的顶部,因此先使用后声明可能会导致逻辑问题

    js分为预编译阶段与执行阶段,

    预编译阶段:

      1 对使用function语句声明的函数进行处理,不仅按照函数名按照变量标识符进行索引,对函数体也进行处理(即对函数名(也可以认为是变量名)进行赋值),如果出现同名函数,则会用后者覆盖前者(即后者的赋值覆盖前者)

        2 对匿名函数在此阶段视而不见

     3 对使用var声明的变量进行索引,但是对变量的初始化忽略掉,

      在预编译阶段遇到var声明的变量,如果前面没有做过初始化,就是undefined,如果该变量在前面是函数名,那么其值就是函数体,这里不做覆盖

function fun(){}
/*变量名fun在预编译阶段,不仅对fun这个变量名进行索引,对其函数体也进行了处理,即fun的值为函数体*/
console.log(fun);//function()
var fun="";
/*使用var声明的变量名fun在预编译阶段,只对其变量名进行索引,不对其进行赋值,所以这fun的初始化值不会覆盖上面的函数体,但是在执行阶段会覆盖*/
console.log(fun);//

    执行阶段:

      1 对匿名函数按表达式逐行进行解释执行

      2 为预编译阶段索引的变量读取初始值,由于执行阶段是逐行解释执行的,所以如果在赋值语句前面进行调用的话,值应该为预编译阶段的

function fun(){
alert(myName);//undefined
var myName="local";
alert(myName);//local
}
fun();

二、for循环

  for循环一般用于遍历数组或者类数组对象(arguments,html容器等)

  优化点:1 对数组的长度进行缓存,var len=arr.length

      2 使用单一的var模式,把所有的变量都提到函数体的开始,使用一个var进行声明

      3 i++替换掉i=i+1与i+=1

      4 在没有要求的时候,递减到0,即i--

  1 一般情况下的for循环

for(var i=0;i<arr.length;i++){
//对数组或者类数组对象中的元素的操作
}

  缺点:显然每次循环都会计算一下要访问数据的长度,这样效率就会降低,尤其是当访问的是HTML容器对象时

  改进:将要访问数据的长度缓存起来,对访问速度的提升相当显著,ie可提高170倍

for(var i=0,max=ayy.length;i<max;i++){
}

  2  进一步改进:结合我们前面提到单var变量模式

function fun(){
var i=0,ayy=[],max;//将所有该函数中用到的变量都声明在函数的顶部
for(i=0,max=ayy.length;i<max;i++){
}
}

  该模式的缺点:复制粘贴的时候,要保证将所需变量的声明全部复制进去

  3 JSLint推荐使用++与--,即逐步递增或者递减(这个有不同的意见,可以保留)

  4 更进一步改进:从最后一个元素,逐个遍历到第一个元素,将i与0比较,比i与非0的max比较效率要高

var a,b,c,d,e,f;
a=+new Date();
for(i=10000000;i>=0;i--){
}
b=+new Date();
console.log(b-a);
// c=+new Date();
for(j=0;j<=10000000;j++){
}
d=+new Date();
console.log(d-c);
//
e=+new Date();
for(k=10000000;k--;){
}
f=+new Date();
console.log(f-e);//

三、for-in循环

  for-in主要用于遍历非数组对象,称之为枚举

1 当要遍历对象属性,并过滤掉原型中的属性和方法时,使用hasOwnProperty()方法

var man={
a:1,
b:2,
c:3
},i; for(i in man){
if(man.hasOwnProperty(i)){//当确定不了对象属性和原型中的内容时,使用hasOwnProperty方法加以判断,如果可以确定则可以省略该判断,提高效率
console.log(i+":"+man[i]);
}
}

  2 在上面我们提到对原型链中的属性和方法进行过滤时,使用hasOwnProperty方法,由于hasOwnProperty方法是属于Object原型的方法,所以我们可以这样使用,这样避免命名冲突(即man对象也有一个hasOwnProperty方法,那么就与我们想要过滤使用的hasOwnProperty方法冲突了)

var i,hasOwn=Object.prototype.hasOwnProperty,man={
"name":"jim",
"age":12
};
for(i in man){
if(hasOwn.call(man,i)){
console.log(i+":"+man[i]);
}
} var i,hasOwn=Object.prototype.hasOwnProperty,man={
"name":"jim",
"age":12,
  hasOwnProperty:function(){console.log("man's hasownproperty")}
};
for(i in man){
  if(man.hasOwnProperty(i)){
    console.log(i+":"+man[i]);
  }
}

四、不要给内置对象的原型增加方法(这里的内置对象指的是Date,Math,Array,String,Event,Object等)

  我们经常给构造函数的原型增一些方法,但是给js的内置对象的构造函数的原型增加方法会严重影响可维护性,因此这里不推荐

  下面的几种情况例外

    1,可以添加ECMAScript5中描述的确尚未实现的方法,等待ECMAScript加以实现

    2,某些浏览器的JS引擎已经实现了该方法

    3,写成文档形式,与团队充分沟通

  给内置对象添加自定义方法

if(typeof Object.prototype.MyMethod!=="function"){
Object.prototype.MyMethod=function(){
};
}  

五、switch模式

  1 每个case语句结尾都有一个break

  2 使用default来作为switch的结束

六、避免使用隐式类型转换

  1 什么是隐式类型转换:false==0、""==false

  2 为了避免出现上面的情况,我们在使用比较语句的时候尽量使用===和!==

  6.1 避免使用eval

    1 eval可以将任何的字符串当作js代码执行

    2 将ajax返回的数据字符串转换成对象时,不推荐使用eval,推荐JSON.parse或者JSON.org网站的类库,因为eval执行的字符串可能是一家被篡改过的代码

    3 setInterval/setTimeout传递参数时,也会导致类似于eval的隐患,因此尽量避免使用 

    4 new Function()与eval和相似,使用时要小心

      如果一定要使用eval,可以使用new Function来替代,因为new Function将在局部函数空间运行,因此代码中var定义的变量不会成为全局变量

      另外一个避免eval中使用var定义的变量成为全局变量,将eval放到一个即时函数中 

      new Function(或者Function),这个方法是只看到全局变量,对局部变量影响较小     

setTimeout("fun(1,2,3)",100);//反模式

//推荐模式
setTimeout(function(){
fun(1,2,3);
},100);
var jsString1='var aaaaaa=1;console.log(aaaaaa);';
var jsString2='var bbbbbbb=2;console.log(bbbbbbb);';
var jsString3='var ccccccc=3;console.log(ccccccc);';
eval(jsString1);//
new Function(jsString2)();//2new Function将在局部函数空间运行

/*另外一个避免eval中使用var定义的变量成为全局变量,将eval放到一个即时函数中*/
(function(){
eval(jsString3);
})();//
console.log(aaaaaa);//1eval代码里面var定义的变量会成为全局变量
console.log(bbbbbbb);//ReferenceError: ccccccc is not defined
console.log(ccccccc);//ReferenceError: ccccccc is not defined
 new Function(或者Function),这个方法是只看到全局变量,对局部变量影响较小

function fff(){
var bbb='bbb';
var jsString='console.log(bbb);'
new Function(jsString)();//或者Function(jsString)()
}
fff();//ReferenceError: bbb is not defined
var a="global a";
function fff(){
var a='local a';
var jsString='console.log(a);'
new Function(jsString)();//或者Function(jsString)()
}
fff();//global a

七、使用parseInt方法时,第二个参数尽量不要省略  

八、编码约定

  8.1 缩进:使用tab键进行缩进

  8.2 大括号:for与if语句最好都使用大括号

  8.3 开放大括号的位置:和语句放在同一行  

/*根据分号插入机制,也就是一行结束如果后面没有分号,会自动插入分号*/
return
{
"name":"Amy",
"age":18
}; /*相当于
return ;
{
"name":"Amy",
"age":18
};
*因此下面这种方式更合适
*/
return {
"name":"Amy",
"age":18
};

九、命名约定

  9.1 构造函数的首字母大写

  9.2 分隔单词:函数/方法名:小驼峰式;变量:小写字母,下划线分隔;常量:全部大写;私有变量/方法:下划线做前缀

十、编写注释

十一、编写API文档

十二、编写可读性强的代码

十三、同行互查

十四、在正式发布时精简代码

十五、运行JSLint

Javascript模式(第二章基本技巧)------读书笔记的更多相关文章

  1. 《Java并发编程实战》第二章 线程安全性 读书笔记

    一.什么是线程安全性 编写线程安全的代码 核心在于要对状态訪问操作进行管理. 共享,可变的状态的訪问 - 前者表示多个线程訪问, 后者声明周期内发生改变. 线程安全性 核心概念是正确性.某个类的行为与 ...

  2. 《DirectX 9.0 3D游戏开发编程基础》 第二章 绘制流水线 读书笔记

    模型的表示 场景:物品或模型的集合 任何物品都可以用三角形网络逼近表示.我们经常用以下术语描述三角形网络:多边形(polygons).图元(primitives).网络几何单元(mesh geomet ...

  3. 《TCP/IP详解卷1:协议》第2章 链路层-读书笔记

    章节回顾: <TCP/IP详解卷1:协议>第1章 概述-读书笔记 <TCP/IP详解卷1:协议>第2章 链路层-读书笔记 <TCP/IP详解卷1:协议>第3章 IP ...

  4. 《企业应用架构模式》(POEAA)读书笔记

    原文地址:<企业应用架构模式>(POEAA)读书笔记作者:邹齐龙(技术-5013 什么是架构 Rolph Johnson认为:架构是一种主观上的东西,是专家级的项目开发人员对系统设计的一些 ...

  5. 重构(Refactoring)技巧读书笔记(General Refactoring Tips)

    重构(Refactoring)技巧读书笔记 之一 General Refactoring Tips, Part 1 本文简要整理重构方法的读书笔记及个人在做Code Review过程中,对程序代码常用 ...

  6. JavaScript高级程序设计第三版-读书笔记(1-3章)

    这是我第一次用markdown,也是我第一次在网上记录我自己的学习过程. 第一章 JavaScript主要由以下三个不同的部分构成 ECMAScript   提供核心语言功能 DOM     提供访问 ...

  7. 《JavaScript面向对象的编程指南》--读书笔记

    第一章.引言 1.5 面向对象的程序设计常用概念 对象(名词):是指"事物"在程序设计语言中的表现形式. 这里的事物可以是任何东西,我们可以看到它们具有某些明确特征,能执行某些动作 ...

  8. 《jQuery实战(第二版)》读书笔记

    第一部分 jQuery核心 1.jQuery基础 第一章总结了jquery的大致功能,基本原理,使用方式. point: (1).引入:<script type="text/javas ...

  9. 高性能MySQL(第4版) 第一章 MySQL架构 读书笔记

    这本书去年11月出的,今年中文版也出了,并且直接上了微信读书,之后有空就读一读,分享下读书笔记~ 原文内容比较充实,建议有时间可以读一下原文. 第一章主要是个概览. MySQL的逻辑架构 默认情况下, ...

随机推荐

  1. 通过ajax 后台传递的 区域id 选中ztree的节点 并展开节点

    代码如下: < script type = "text/javascript" >    var flag = "<%=request.getParam ...

  2. 【转】Thread.sleep(0)的意义

    Thread.sleep(0)的意义 2012-03-23 17:47 2188人阅读 评论(2) 收藏 举报 windows算法unixthread 我们可能经常会用到 Thread.Sleep 函 ...

  3. SQL注入的字符串连接函数

    在select数据时,我们往往需要将数据进行连接后进行回显.很多的时候想将多个数据或者多行数据进行输出的时候,需要使用字符串连接函数.在sqli中,常见的字符串连接函数有concat(),group_ ...

  4. Valid Palindrome ---- LeetCode 125

    Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignori ...

  5. C#单链表

    顺序表是用地址连续的存储单元顺序存储线性表中的各个数据元素, 逻辑上相邻的数据元素在物理位置上也相邻.因此,在顺序表中查找任何一个位置上的数据元素非常方便, 这是顺序存储的优点. 但是, 在对顺序表进 ...

  6. Eclipse默认空间与工作空间的更改(转)

    一.更改eclipse默认空间 进行 eclipse 目录下的 configuration 目录, 打开config.ini文件 将 osgi.instance.area.default= 项修改成你 ...

  7. 题目: 求1+2+...+n,要求不使用乘除发、for、while、if、else、switch、case、等关键字以及条件判断语句(A?B:C)

    #include <iostream> using namespace std; int add_(int a,int b){ return 0; } int Add(int i,bool ...

  8. bookstores网上书店测试缺陷报告1

    Bookstore网上书店系统测试缺陷报告   缺陷编号 01.01.0001 发现人 吴赵昕 记录日期 2016-06-10 所属模块 购物车 确认人 吴赵昕 确认日期 2016-06-10 当前状 ...

  9. 传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确。此 RPC 请求中提供了过多的参数。最多应为 2100

    出现这个问题的背景是,判断一批激活码在系统中是否已经存在,很傻的一个作法是,把这一批激活码,以in(in (‘ddd‘,‘aaa‘))的形式来处理,导致问题的出现. 后来,查找资料,http://bb ...

  10. PHP中FOREACH()用法

    PHP 4 引入了 foreach 结构,和 Perl 以及其他语言很像.这只是一种遍历数组简便方法.foreach 仅能用于数组,当试图将其用于其它数据类型或者一个未初始化的变量时会产生错误. 1. ...