《JavaScript 模式》读书笔记(2)— 基本技巧2
前一篇,简单介绍了一些js代码的基本技巧。那么这篇文章,我们继续后续的内容。
一、for循环
for循环经常用在遍历数组或者类数组对象,如引数(arguments)和HTML容器(HTMLColltion)对象。通常for循环模式使用如下:
for(var i = 0; i < myarray.length; i++) {
// 对myarray[i]做操作
}
这种模式的问题在于,每次循环迭代时都要访问数据的长度。这样不好。所以:
for(var i = 0, max = myarray.length; i < max; i++) {
// 对myarray[i]做操作
}
我们可以这样,先把长度存储起来,这样就不会每次循环都要去访问数据的长度了。尤其在一些DOM操作下,访问的速度很慢。
下面是单变量模式,也可以将变量放到循环以外:
function looper() {
var i = 0,
max,
myarray = [];
// ...
for (i = 0,max = myarray.length; i < max; i++){
//处理myarray[i]
}
}
这种模式的好处在于一致性,因为它贯穿了单一变量的模式。缺陷在于创建代码时黏贴和复制整个循环比较麻烦。例如,如果要从一个函数复制循环至另一个函数,必须确保能将i和max携带至新函数中(如果这几个量在愿函数中不再需要,则很可能会删除掉它们了)。注意,这里的复制并不是说单纯的复制代码至另一处,而是通过js的代码复用模式在另一个函数中使用。
对于循环的最后一个改进,时使用i++代替一下两种表达式:
- i = I + 1
- i += 1
for模式重的两个变量引出了一些细微的操作,原因是:
- 使用了最少的变量(而非最多)
- 逐步减至0,这样通常更快,因为同0比较比同数组的长度比较,或同非0数组的比较更有效率。
第一个修改后的模式:
var i ,myarray = [];
for(i = myarray.length; i--;){
// 处理myarray[i]
}
第二个使用while循环:
var myarray = [],i = myarray.length;
while(i--){
//处理myarray[i]
}
二、for-in循环
for-in循环应该用来遍历非数组对象。使用for-in循环也被成为枚举(enumeration)。从技术上来说,可以使用for-in循环来遍历数组(因为在JavaScript中,数组也是对象),但是不推荐这样做,因为当该数组对象已经被自定义函数扩大后,这样做有可能会导致逻辑上的错误。
var myarray = ['a','b','c','d'];
for(var i in myarray) {
console.log(i)
console.log(myarray[i])
}
当遍历对象属性来过滤遇到原型链的属性时,使用hasOwnProperty()方法时非常重要的。比如:
var man = {
hands:2,
legs:2,
heads:1
}
//代码的其他部分
//将一个方法添加到所有对象上
if(typeof Object.prototype.clone === 'undefined'){
Object.prototype.clone = function () {};
}
// 反模式
for(var i in man) {
console.log(i + ':' + man[i])
}
或者,这样才是你想要的:
var man = {
hands:2,
legs:2,
heads:1
}
//代码的其他部分
//将一个方法添加到所有对象上
if(typeof Object.prototype.clone === 'undefined'){
Object.prototype.clone = function () {};
}
for(var i in man) {
if(man.hasOwnProperty(i)) {
console.log(i + ':' + man[i])
}
}
那多说一句,Object.hasOwnProperty()方法是干什么的?方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。这是MDN上的说法。也就是说,该方法会告诉你,该属性是否是该对象自身的属性,而非其他来源(比如原型链上的属性)。
另一种使用hasOwnProperty()的模式是在Object.property中调用该函数,比如:
for(var i in man) {
if(Object.prototype.hasOwnProperty.call(man,i)) {
console.log(i + ':' + man[i])
}
}
在使用hasOwnProperty对man对象进行精炼后,可以有效的避免命名冲突,也可以使用一个本地变量来缓存比较长的属性名,比如:
var i,
hasOwn = Object.prototype.hasOwnProperty; for(i in man) {
if(hasOwn.call(man,i)) {
console.log(i + ':' + man[i])
}
}
注意:严格来讲,不使用hasOwnProperty()并没有错。依赖于具体任务和对代码的自信,您可以略过该方法并稍微加速循环的执行速度。但是当确认不了对象的内容(和原型链)时,最好还是加上hasOwnProperty()这样的安全检查。
有一种格式化的变种,但是个人极不推荐这样使用,因为:可读性极差,无法通过JSLint检查:
var i,
hasOwn = Object.prototype.hasOwnProperty; for(i in man) if(hasOwn.call(man,i)) {
console.log(i + ':' + man[i])
}
唯一的优点就是省了一个花括号?
三、不要增加内置的原型
增加构造函数的原型属性是一个增强功能性的强大的方法,但是有时候该方法会过于强大。增加内置构造函数(比如Object(),Array(),Function()等)的原型是很有诱惑力的,但是这可能严重影响可维护性,因为这种做法将使代码变得更加不可预测。
因此,最好的方法就是不要给内置的原型增加属性,除非:1、未来的ECMAScript版本或JavaScript的具体实现可能将该功能作为一个统一的内置方法时。2、如果检查了自定义的属性或方法并未存在时。或者为了统一不同浏览器或相同浏览器的不同版本而进行的兼容时,前提是要检查是否存在。3、文档记录,并与团队沟通清楚。
如果确实遇到上面的场景,你可以这样:
if(typeof Object.prototype.myMethod !== 'function'){
Object.prototype.myMethod = function () {
// implementation
}
}
四、switch模式
说到switch,我要多说一句,很多新手都会纠结是if-else性能更好,还是switch性能更好。首先,抛却具体场景谈性能,就是耍流氓。其次,switch在c++这样的语言中,性能确实要稍微好一点。但是,在JavaScript中,两者几乎无任何区别,而且switch还可能会引起意外的问题,所以建议使用if-else。
你如果你确定switch是一个更好的选择,那么你可以这样:
var inspect_me = 0,
result = '';
switch (inspect) {
case 0:
result = 'zero';
break;
case 1:
result = 'one';
break;
default:
result = 'unknow';
break;
}
通常,格式是这样的:
- 使每个case和switch纵向排列整齐(打括号的缩紧是个例外)。
- 在每个case中使用代码缩进。
- 在每个case语句结尾有个明确的break语句。
- 避免使用fall-throughs(也就是有意不使用break语句,以使得程序会按顺序一直向下执行)。如果确实希望使用fall-throughs,那么请确信在代码中使用fall-throughs是最好的途径,因为当别人阅读这段代码时会认为这是一个错误。
- 用default语句来作为switch的结束:当以上场景都不匹配时,给出一个默认的结果。
五、避免使用隐式类型转换
不要在你的程序中使用==或者!==,请使用===和!===。因为JavaScript在使用比较语句时会执行隐式类型转换。转换的场景可能比你知道的要多得多。
六、避免使用eval()
eval()是个魔鬼,它可以实现你的梦想,也可以夺走你的灵魂。所以,请一定避免在你的代码中使用eval()。
该函数可以将任意字符串当作一个JavaScript代码来执行。当需要讨论的代码是预先就编译好的(不是在动态运行时决定),是没有理由需要使用eval()的。而如果代码是在运行时动态生成的,则也有其他更好的方法来代替eval()实现其功能。比如:
// 反模式
var property = 'name';
alert(eval("obj." + property)); // 推荐的方法
alert(obj[property])
使用eval()也包含一些安全隐患,因为这样做有可能执行被篡改过的代码(例如来自网络的代码)。
还有一点需要牢记的是。通过setInterval(),setTimeout()和function()等构造函数来传递参数,在大部分情形下,会导致类似eval()的隐患,因此应尽量避免使用这些函数。在幕后,JavaScript仍然不得不评估和执行以程序代码方式传递过来的字符串:
// 反模式
setTimeout('myFunc()',1000);
setTimeout('myFunc(1,2,3)',1000); // 推荐的模式
setTimeout(myFunc,1000);
setTimeout(function() {
myFunc()
},1000)
使用new Function()构造函数和使用eval()比较类似,因此该函数的使用也需要十分小心,该函数是一个功能强大的函数,但是通常容易被误用。如果一定需要使用eval(),那么可以考虑使用new Function()来代替。这样做的一个潜在好处是由于在new Function()中的代码将在局部函数空间中运行,因此代码中任何采用var定义的变量不会自动成为全局变量。另一个避免自动成为全局变量的方法是将eval()调用封装到一个即时函数中。
看下面的代码,在这里只有un这个变量仍然是一个全局变量,会影响到命名空间:
console.log(typeof un);
console.log(typeof deux);
console.log(typeof trois); var jsstring = "var un = 1; console.log(un)";
eval(jsstring); jsstring = "var deux = 2; console.log(deux)";
new Function(jsstring)(); jsstring = "var trois = 3; console.log(trois)";
(function() {
eval(jsstring);
}()); console.log(typeof un);
console.log(typeof deux);
console.log(typeof trois);
另一个new Function()和eval()的区别是eval()会影响到作用域链,而Function更多的类似于一个沙盒。无论在哪里执行Function,它都仅仅能看到全局作用域。因此对局部变量的影响比较小。下面的例子,eval()可以访问和修改它外部作用域的变量,然而Function不行(请注意使用Function和使用new Function是一样的)。
(function () {
var local = 1;
eval("local = 3;console.log(local)");
console.log(local);
}());
(function() {
var local = 1;
Function("console.log(typeof local);")();
}())
七、使用parseInt()的数值约定
通过使用paresInt(),可以从一个字符串中获取数值。该函数的第二个参数是一个禁止参数,通常可以忽略该参数,但是最好不要这样做。因为当解析的字符串是0开头,就会出现错误:例如在处理日期是只有一部分日期会进入字段。在ECMAScript 3中,0开始的字符串会被当作一个八进制数,而在ECMAScript 5中发生了改变。为了避免不一致性和为预期的结果,请每次都指定具体进制参数:
var month = '06',
year = '09';
month = parseInt(month,10);
year = parseInt(year,10);
console.log(month,year);
另一个将字符串转换成数值的方法是:
Number('01');
这种方法通常会比parseInt()快很多,因为正如其名称一样,parseInt()是解析而不是简单的转换。但是如果希望“08 hello”会返回一个数值,那么除了parseInt()之外,其他方法都会失败并返回NaN。
这篇内容比较多,但是在实际的工作中十分具有价值。下一篇会讨论一些规范和约束,比如命名,打括号,缩进空格等在编码中的处理。
《JavaScript 模式》读书笔记(2)— 基本技巧2的更多相关文章
- JavaScript模式读书笔记 文章3章 文字和构造
1.对象字面量 -1.Javascript中所创建的自己定义对象在任务时候都是可变的.能够从一个空对象開始,依据须要添加函数.对象字面量模式能够使我们在创建对象的时候向其加入函数. ...
- JavaScript模式读书笔记 第4章 函数
2014年11月10日 1.JavaScript函数具有两个特点: 函数是第一类对象 函数能够提供作用域 函数即对象,表现为: -1,函数能够在执行时动态创建,也 ...
- 《你不知道的javascript》读书笔记2
概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. 这篇笔记是这本书的下半部分,上半部分请见<你不知道的java ...
- 《编写可维护的javascript》读书笔记(中)——编程实践
上篇读书笔记系列之:<编写可维护的javascript>读书笔记(上) 上篇说的是编程风格,记录的都是最重要的点,不讲废话,写的比较简洁,而本篇将加入一些实例,因为那样比较容易说明问题. ...
- Javascript & JQuery读书笔记
Hi All, 分享一下我学JS & JQuery的读书笔记: JS的3个不足:复杂的文档对象模型(DOM),不一致的浏览器的实现和便捷的开发,调试工具的缺乏. Jquery的选择器 a. 基 ...
- SQL反模式读书笔记思维导图
在写SQL过程以及设计数据表的过程中,我们经常会走一些弯路,会做一些错误的设计.<SQL反模式>这本书针对这些经常容易出错的设计模式进行分析,解释了错误的理由.允许错误的场景,并给出更好的 ...
- Javascript模式(第二章基本技巧)------读书笔记
本章主要帮助大家写出高质量的JS代码的方法,模式和习惯,例如:避免使用全局变量,使用单个的var变量声明,缓存for循环的长度变量length等 一.尽量避免使用全局变量 1 每一个js环境都有一个全 ...
- 《Javascript模式》之对象创建模式读书笔记
引言: 在javascript中创建对象是很容易的,可以使用对象字面量或者构造函数或者object.creat.在接下来的介绍中,我们将越过这些方法去寻求一些其他的对象创建模式. 我们知道js是一种简 ...
- JavaScript设计模式:读书笔记(未完)
该篇随我读书的进度持续更新阅读书目:<JavaScript设计模式> 2016/3/30 2016/3/31 2016/4/8 2016/3/30: 模式是一种可复用的解决方案,可用于解决 ...
- 《面向对象的JavaScript》读书笔记
发现了2004年出版的一本好书,用两天快速刷了一遍,草草整理了一下笔记,在此备忘. 类:对象的设计蓝图或制作配方. 对象 === 实例 :老鹰是鸟类的一个实例 基于相同的类创建出许多不同的对象,类更多 ...
随机推荐
- mysql 子查询 合并查询
4.1带In 关键字的子查询 一个查询语句的条件可能落在另一个SELECT 语句的查询结果中. SELECT * FROM t_book WHERE booktypeId IN (SELECT id ...
- iOS应用程序开发——解决iOS7之前版本与之后版本下app启动图片跳动问题
之前开发的app都是针对iOS6(兼容iOS5),所以在开发之初也出现了启动图片向下跳动的现象,通过网上的解决方法,代码中做了处理(“-20”): _imageView.frame = CGRectM ...
- HttpClient-get请求/Post请求/Post-Json/Header
1.Pom文件添加httpClient 依赖 <dependency> <groupId>org.apache.httpcomponents</groupId> & ...
- JS去重函数的扩展应用
数据: 账单id[1,1,1,1,2,2,2,3,3,3,3,3,3,3], 相对于账单id的金额[100,120,110,150,200,180,220,115,150,120,180,220,14 ...
- GOM通区插件-支持GOM绝对路径-读取配置项-分割字符等功能。不定期更新
A-A+ 2019年07月19日 Gom引擎 阅读 45 views 次 [@Main] #IF #SAY [<读配置项/@读配置项>] [<写配置项/@写配置项>] [& ...
- jmeter+ant+jenkins 接口自动化测试持续集成(送源码)
9.1 安装和介绍 JMeter 安装文件路径:https://pan.baidu.com/s/1kVJdnuv. JMeter 是轻量级的开源且稳定的自动化测试工具. 思路:在接口说明文档中整理 ...
- ES:PB级别的大索引如何设计
一.单个大索引的缺陷 如果每天亿万+的实时增量数据呢,基于以下几点原因,单个索引是无法满足要求的: 1.存储大小限制维度 单个分片(Shard)实际是 Lucene 的索引,单分片能存储的最大文档数是 ...
- SecureCRT语法高亮设置
因为默认情况下,SecureCRT不能显示语法高亮特性,整个界面颜色单一,看起来不爽,也没有效率,所有通过设置一下语法高亮还是很有必要的, 默认字体也看着不是很清晰,还是更改为我比较喜欢的Courie ...
- resourcequota分析(一)-evaluator-v1.5.2
什么是evaluator 大家都知道,Kubernetes中使用resourcequota对配额进行管理.配额的管理涉及两个步骤:1.计算请求所需要的资源:2.比较并更新配额.所以解读resource ...
- 启动tomcat报错 Unable to process Jar entry [module-info.class] from Jar...[xxx.xx.jar!\] for annotations
Java Web 项目tomcat启动报错module-info.class 从git 上面拉下的项目,运行报错. jdk.maven配置正常 tomcat启动遇见的问题: Unable to pro ...