精读JavaScript模式(二)
我在想知识点怎么去分类,原本计划一章节一篇,但这样会会显得长短不一。更主要的是看到哪写的哪更为随意。那么这一篇还是紧接第一篇进行知识梳理,上篇说到了更优化的for循环,现在继续聊聊其它的循环方式。
1.for-in循环
for-in循环又称之为枚举,常用于遍历非数组对象。虽然也可以用于遍历数组,但并不推荐这么去做,例如在for-in循环key值为数字的对象时,遍历顺序其实是不固定的,这点与for不同。
var obj = { 3:"echo", 2:"25", 1:"web", 4:'male' } //for-in for(var key in obj){ console.log(key+':'+obj[key])//1:web 2:25 3:echo 4:male };
当我们遍历对象时其实存在一个问题,我们都知道对象存在继承,那么在遍历过程中很有可能拿到你不是你定义的而是通过原型链继承而来外来属性,举个例子。
//假设其他人在原型上加添加了一个克隆方法 Object.prototype.clone = function () {}; //自己定义的对象 var man = { hands:2, legs:2, heads:1 }; //我想遍历出自己定义man的所有属性 for(var i in man) { console.log(man[i]);// 2 2 1 ƒ () {} }
很明显clone方法并不是我们想要的,那怎么去除掉原型链继承而来的属性呢,这里就需要使用hasOwnProperty方法来对对象属性做一个判断。
hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性---MDN了解更多
//假设其他人在原型上加添加了一个克隆方法 Object.prototype.clone = function () {}; //自己定义的对象 var man = { hands:2, legs:2, heads:1 }; //我想遍历出自己定义man的所有属性 for(var i in man) { //写法一, if (man.hasOwnProperty(i)) { console.log(man[i]);// 2 2 1 } //写法二 if (Object.prototype.hasOwnProperty.call(man, i)) { console.log(man[i]);// 2 2 1 } }
方法二相对方法一的好处是,万一有人给man属性重定向了一个hasOwnProperty方法,则会让属性验证方法失效。
我们在这里提到hasOwnProperty方法只是更为保存的做法,如果你对于代码足够自信,即便省略掉这个判断也不是错误的,只是根据实情来使用。
2.尽量少的扩充内置原型(Object.prototype)
var obj = {}; console.log(obj);
//方法一 var obj = Object.create(null); console.log(obj)//{} 真正的空对象 //方法二 var obj = {}; Object.setPrototypeOf(obj,null); //参数一 将被设置原型的对象. 参数二 该对象新的原型链 console.log(obj)//{} 真正的空对象
3.switch模式(switch case)
var a = 1, result = ''; //if else写法 if(a === 1){ result = "a"; }else if (a === 2){ result = "b"; }else if (a ===3){ result = "c"; }; //switch case写法 switch (a) { case 1 : result = 'a'; break; case 2 : result = "b"; break; case 3: result = "c"; break; default: result = "unknown"; };
使用switch case还有一种场合,假设当a为1,2其中的一个时,我需要执行方法fn1,当a为3,4其中一个时,我需要调用方法fn2,如果都不是,那就执行方法fn3,这里用switch case看起来会更为整洁。
//方法一 if(a === 1 || a === 2){ fn1()//do something }else if(a === 3 || a === 4) { fn2()//do something }else{ fn3()//do something } //方法二 switch (a) { case 1 : case 2 : fn1();//do something break; case 3 : case 4 : fn2()//do something break; default: fn3()//do something };
4.避免隐式类型转换(===与==)
在javaScript的比较操作中会存在一些隐式的数据类型转换。例如false == 0 "" ==0的比较都会返回true;"2"-1 =1,"2"+1 = "21"等等。
在数据比较中,为了避免数据隐式转换造成的干扰,推荐使用===和!===运算符,它们相比== !==会比较值以外,还会比较数据的类型。
var a = 0; a == "" ? console.log('请输入有效内容') : fn();//请输入有效内容 因为0 == ""为true
很明显,在这里假设用户输入了一个有效数字0,还是不会调用我们的方法,使用===和!===会更为严谨。
5.避免使用eval()
eval()
函数会将传入的字符串当做 JavaScript 代码进行执行。---MDN了解更多
var fn = "(function (){console.log(1)})()" //即使fn存储了一个字符串的自调函数,eval也能运行 eval(fn)//1
很显然,fn只是一个自调函数写法的字符串,但是用了eval却将这个字符串当方法执行了。
定时器大家都不会陌生,当我们在定时器中调用一个外部定义的方法其实有两种写法:
function fn() { console.log(1); }; //反模式 setInterval("fn()",1000); //推荐 setInterval(fn,1000);
两种写法都会执行,有没有很奇怪,为什么第一种加了引号明明是个字符串,为什么还是会调用,因为这个写法和eval()原理类似,同样不推荐这种写法。
那如果我们真的要执行一段字符串怎么办呢?这里推荐使用new Function()或者自调函数来包裹原本的eval()方法。
var str1 = "var a = 1;console.log(a)"; eval(str1); //使用new Function var str2 = "var b = 2;console.log(b)"; new Function(str2)();//或者 Fcuntion(str2)()也可以 //使用自调函数包裹 var str3 = "var c = 3;console.log(c)"; (function (){eval(str3)})(); console.log(typeof a);//number console.log(typeof b);//undefined console.log(typeof c);//undefined
在上述代码中,字符串均可以被转化为代码执行,但是好处在于,new Function与自调函数的写法就像一个沙箱,它不会修改作用域链,并不会污染局部作用域,所以例子中除了a都不能被外部作用域使用。
6.字符串与数字的转换(parseInt)
parseInt:该可解析一个字符串,并返回一个整数。
parseFloat:该函数指定字符串中的首个字符是否是数字。如果是,则对字符串进行解析,直到到达数字的末端为止,然后以数字返回该数字,而不是作为字符串。
编程中将字符串转变为数字是很常见的,比如我们要将"100"转为数字100:
var str = "100"; console.log(parseInt(str));//100 console.log(parseFloat(str));//100 console.log(+str);//100 console.log(Number(str));//100
这四种方法中使用+或者Number()会比parseInt以及parseFloat要更快,因为前者只是单纯的转换,而后者是在做字符串的解析。
不过,如果你要处理类似"100 echo"这样的字符串,就只能使用parseInt或者parseFloat方法了。
var str = "100 echo"; console.log(parseInt(str));//100 console.log(parseFloat(str));//100 console.log(+str);//NAN console.log(Number(str));//NAN
如果是"100.11"带小数点的字符串,就不能使用parseInt()方法,因为当它遇到非数字就会停止解析,这样会截掉小数点后面的数字。
var str = "100.11"; console.log(parseInt(str));//100 console.log(parseFloat(str));//100.11 console.log(+str);//100.11 console.log(Number(str));//100.11
到这里第二章一些挺实用,挺好玩的东西基本记录完成了,第三章也会继续写,可能要点时间,因为中途也要做一点别的东西,最近学习的效率很低,确实得反省自己,目标还很远,加油吧。