精读JavaScript模式(二)


风晓
风晓 2024-01-01 19:38:05 67965 赞同 0 反对 0
分类: 资源
继续聊聊其它的循环方式。

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)

几乎所有的 JavaScript 对象都是 Object 的实例;一个典型的对象继承了Object.prototype的属性(包括方法),尽管这些属性可能被覆盖。
我们可以利用这一点为构造函数添加新的方法,例如为内置的构造函数Object() Array()  Function()扩充原型,这听起来非常棒,但这种做法会严重降低代码的可维护性,让你的代码变得难以预测。
例如在上一个例子中,我们为Object.prototype自定义添加了一个clone方法,这对于不使用hasOwnProperty()的开发者来说无疑是一个噩梦。
当我们新建一个空对象时,其实它并没有我们想象的那么干净。
var obj = {};
console.log(obj);

那怎么样才能创建一个不具备继承原型链属性的空对象呢,其实可以通过Object.create(null)实现,或者通过Object.setPrototypeOf方法改变原型链。
复制代码
//方法一
var obj = Object.create(null);
console.log(obj)//{}  真正的空对象

//方法二
var obj = {};
Object.setPrototypeOf(obj,null); //参数一 将被设置原型的对象.  参数二 该对象新的原型链
console.log(obj)//{} 真正的空对象
复制代码

3.switch模式(switch case)

 条件判断,我们可能首先想到的就是最好用的if else,不得不说,简单的条件判断,用if else可读性还是很强的,但对于条件较多的情况,多个else代码难免看起来臃肿。合理的使用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"等等。

在数据比较中,为了避免数据隐式转换造成的干扰,推荐使用===和!===运算符,它们相比==  !==会比较值以外,还会比较数据的类型。

假设有一个input,当用户没输入任何东西时,我们给出提示,否则执行某个方法
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

如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!

评价 0 条
风晓L1
粉丝 1 资源 2038 + 关注 私信
最近热门资源
麒麟系统版本介绍白皮书  516
MiSans 阿拉伯语字体文件  459
解决新版本麒麟系统中微信打开白屏显示  400
麒麟系统进行系统监控,查看进程的运行时间来优化性能  332
临时关闭swap分区与永久关闭swap分区(注意必须确保系统有足够内存运行!)  224
统信桌面专业版添加字体  217
统信uos单一程序黑屏,任务栏正常显示解决办法  216
统信uos快捷键文档  187
统信系统双无线网卡设置关闭开启单一网卡  146
分享一个磁盘恢复工具,适用于多平台(包括统信)  122
最近下载排行榜
麒麟系统版本介绍白皮书 0
MiSans 阿拉伯语字体文件 0
解决新版本麒麟系统中微信打开白屏显示 0
麒麟系统进行系统监控,查看进程的运行时间来优化性能 0
临时关闭swap分区与永久关闭swap分区(注意必须确保系统有足够内存运行!) 0
统信桌面专业版添加字体 0
统信uos单一程序黑屏,任务栏正常显示解决办法 0
统信uos快捷键文档 0
统信系统双无线网卡设置关闭开启单一网卡 0
分享一个磁盘恢复工具,适用于多平台(包括统信) 0
作者收入月榜
1

prtyaa 收益399.62元

2

zlj141319 收益236.11元

3

IT-feng 收益219.61元

4

1843880570 收益214.2元

5

风晓 收益208.24元

6

哆啦漫漫喵 收益204.5元

7

777 收益173.07元

8

Fhawking 收益106.6元

9

信创来了 收益106.03元

10

克里斯蒂亚诺诺 收益91.08元

请使用微信扫码

添加我为好友,拉您入交流群!

请使用微信扫一扫!