温故js系列(8)-详解代码中的流程掌握

前端进修:教程&开辟模块化/范例化/工程化/优化&东西/调试&值得关注的博客/Git&口试-前端资本汇总

迎接提issues指正:流程掌握

JavaScript-流程掌握

JavaScript是单线程的,一个语句一个语句的实行。语句是实行过程当中的流程、限制与商定,情势上可所以单行语句,也许由一对大括号”{}”括起来的复合语句,复合语句团体能够作为一个单行语句处置惩罚。那末,代码中,流程掌握就显得分外重要了。JavaScript也划定了一些语句和一些关键字用于流程掌握。

if语句

if (前提表达式) {语句}

if(2 > 1){
    console.log('xzavier win');
}

javascript会推断括号里的前提表达式的值。假如值为truthy范例的值,也就是真,则实行背面的一条语句,不然不实行。这个语句无可厚非,运用率也是极高的,有时刻会运用短衔接来替换:

2 > 1 && console.log('xzavier win');

想相识此运算符以及更多运算符参考: 运算符详解

关于推断拜见本系列文章:代码中的那些推断

if (前提表达式) {语句;} else {语句;}

if为真值(Boolean转换),则实行if里的代码,不然实行else里的代码。

if (2 > 1) {
    console.log('xzavier win'); 
} else {
    console.log('xzavier fail'); 
}

这个语句的运用也无需多说,有时刻会运用三目运算符替代:

2 > 3 ? console.log('xzavier win') : console.log('xzavier fail');

有时刻设想到赋值的if…else,也能够运用短衔接。同参考上面一篇文章。

if (前提表达式) {语句;} else if (前提表达式) {语句;} … else {语句;}

if (1 > 2) {
    console.log('xzavier win'); 
} else if(3 > 2){
    console.log('xzavier win2'); 
} else {
    console.log('xzavier fail'); 
}

JavaScript实际上是没有else if的,else if算是一种封装。看这里的js推断:

var a = 3, b = 2;
a > b // true
a > b // false
a = b // false

额(⊙o⊙)…这不是在逗我吗?不,我只是用最简朴的推断说一下js的推断:

a >= b // true  
//实在它是先推断了 a < b 为false 再用“非正即反” 来返回 a >= b 的值

不信?看这个:

var a = {};  // 你能够增加恣意属性 {x:3};
var b = {};  // 你能够增加恣意属性 {z:2};;
a < b;    // false
a == b;   // false
a > b;    // false

对象的比较,能够拜见本系列推断相干的文章。
看上面没问题是吧,然则用 >= 就不一样了:

a <= b;    // true
a >= b;    // true

我…所以,也依据言语范例,先对 <= 的另一面求值,再“非正即反”。

那末回到else if呢,上面的代码实在终究是如许的:

if (1 > 2) {
    console.log('xzavier win'); 
} else {
     if(3 > 2){
        console.log('xzavier win2'); 
    } else {
        console.log('xzavier fail'); 
    }
}

把它拆分为多个if…else… 这只是个探讨,因为else if屡见不鲜,代码又少,语义很好,所以运用很多。所以,恣意的运用吧,我的意义是别避开写else…if…,不是写一大堆else…if…,假如有一堆else…if…,那照样用下面的switch吧

switch语句

switch 语句是多重前提推断,用于多个值相称的比较。

var xzavier = 'boy';
switch (xzavier) {
    case 'girl' :
        console.log('xzavier is a girl');
        break;
    case 'boy' :
        console.log('xzavier is a boy');
        break;
    case 'man' :
        console.log('xzavier is a man');
        break;
    default : 
        console.log('time error');
}

if和switch之间能够转换,当前提过多时,运用switch能够让代码更清楚,更悦目。

这里说一下switch,它对括号里的语句求值一次,然后将返回值与每一个case表达式举行婚配。假如找到一个婚配,就会最先实行谁人婚配的case里的代码,直到碰到一个break也许直到switch块末端。
所以,写好break,很重要:

var xzavier = 'boy';
switch (xzavier) {
    case 'girl' :
        console.log('xzavier is a girl');
        break;
    case 'boy' :
        console.log('xzavier is a boy');
    case 'man' :
        console.log('xzavier is a man');
        break;
    default : 
        console.log('time error');
}
VM293:7 xzavier is a boy
VM293:9 xzavier is a man

没有break掉,你的营业代码能够就涌现bug.运用switch就须要好好的斟酌营业场景,该break的处所切勿遗忘。

别的,表达式的返回值和每一个case表达式之间的婚配推断运用的是全等运算符===

var xzavier = '1';
switch (xzavier) {
    case 1 :
        console.log('xzavier is a girl');
        break;
    case 2 :
        console.log('xzavier is a boy');
    case 3 :
        console.log('xzavier is a man');
        break;
    default : 
        console.log('type error');
}
// type error

另有一点就是 default 非必须,位置也能够不牢固。因为js老是先去婚配表达式的返回值和每一个case表达式,终究没有找到婚配的case,就会寻觅default,找到了则实行default里的语句。没找到则实行到switch块末端。

do…while语句

do…while 语句是一种先运转,后推断的轮回语句。也就是说,不论前提是不是满足,最少先运转一次轮回体。

var xzavier = 1; 
do {
    console.log(xzavier);
    xzavier++;
} while (xzavier <= 10); 
// 1 2 3 4 5 6 7 8 9 10

var xzavier = 1; 
do {
    console.log(xzavier);
    xzavier++;
} while (xzavier <= 0);//先运转一次,再推断
// 1

while语句

while 语句是一种先推断,后运转的轮回语句。

var xzavier = 1; 
while (xzavier <= 10) {  //先推断,再运转
    console.log(xzavier);
    xzavier++;
}
// 1 2 3 4 5 6 7 8 9 10

var xzavier = 1; 
while (xzavier <= 0) {  //先推断,再运转
    console.log(xzavier); // 不会实行
    xzavier++;
} 

for语句

for 语句也是一种先推断,后运转的轮回语句。但它具有在实行轮回之前初始变量和定义轮回后要实行代码的才能。

for (var i = 0; i <= 10 ; i++) { 
    console.log(i); 
} 

第1步: 声明变量var i = 1;
第2步: 推断i <= 10
第3步: console.log(i);
第4步: i++
第5步: 反复2-5,直到推断为false

这里我们经常会碰到如许的写法:

for (var i = 0; i <= data.length ; i++) {

发起先缓存data.length 为一个变量:

for (var i = 0, l = data.length; i <= l ; i++) {

如许做的目标并非肯定能提拔顺序的机能,因为length属性是一个字典属性,读取它和读取变量的复杂度都为O(1)
如许做主如果为了防备在for轮回中转变了data.length,详细运用还应视详细代码而定。

固然,假如涉及到DOM,那末缓存变量就肯定能提拔代码机能了。

for (var i = 0; i <= $('.item').length ; i++) {

每轮回一次都$('.item') 就相对消耗机能了,与DOM有关的读取大多状况都应先用变量缓存,特别营业视状况而定。

var ietm_l = $('.item').length;
for (var i = 0; i <= ietm_l ; i++) {

for…in语句

for…in 语句能够用来罗列对象的属性。

var xzavier = { 
    'name' : 'xzavier', 
    'age' : 23,
    'job' : 'Jser',
    'width' : 100,
    'height' : 100,
    'border' : 10
};
for (var i in xzavier) { 
    console.log(i);
}
//name age job width height border
for (var i in xzavier) { 
    console.log(xzavier[i]);
}
//xzavier 23 Jser 100 100 10

for…in是为遍历对象属性设想的,然则它能够遍历数组,我去,因为数组也是对象啊。 不过,for…in 轮回遍历的是对象的属性,而不是数组的索引。我们看一下打印的结果就晓得了:

var arr = [1,2,3];
for(var i in arr) {
    console.log(typeof(i));
}
// string string string

而典范的for语句遍历是数组的索引:

var arr = [1,2,3];
for(var i = 0; i < arr.length; i++) {
    console.log(typeof(i));
}
// number number number

我们用for…in遍历数组时,取arr[i]时是我们希冀取得的结果:

var arr = [1,2,3];
for(var i in arr) {
    console.log(i + '--' + arr[i]);
}
// 0--1 1--2 2--3

然则:

var arr = [1,2,3];
arr.name = 'xzavier';
for(var i in arr) {
    console.log(i + '--' + arr[i]);
}
VM230:4 0--1
VM230:4 1--2
VM230:4 2--3
VM230:4 name--xzavier

它接见了数组新增的 “name” 属性,因为 for-in 遍历了对象的一切属性。

以至包含原型链上的一切可罗列的属性:

var arr = [1,2,3];
    arr.name = 'xzavier';Array.prototype.oname = 'xzavier.chris'
    for(var i in arr) {
        console.log(i + '--' + arr[i]);
    }
VM236:4 0--1
VM236:4 1--2
VM236:4 2--3
VM236:4 name--xzavier
VM236:4 oname--xzavier.chris

明显,我们习气的数组遍历的结果是只要1,2,3 如许的结果的。

所以,综合来讲:
for…in 轮回遍历的是对象的属性,而不是数组的索引。正如例子,输出的索引值 “0”、 “1”、 “2”不是 Number 范例的,而是 String 范例的,因为是对象的属性都是string范例。

如许看来,for…in是不适合遍历数组的。确切,for…in原本就是为遍历对象属性设想的。

不过,在关于比较特别的数组,for…in也许有效:

var arr = Array(1000), count = 0;
arr[100] = 100;
arr[200] = 200;
arr[300] = 300;
for(var i in arr) {
    count += 1;
    console.log(i + '--' + arr[i]);
}
console.log(count);
VM242:7 100--100
VM242:7 200--200
VM242:7 300--300
VM242:9 3   // 只轮回了3次额

而for轮回:

var arr = Array(1000), count = 0;
arr[100] = 100;
arr[200] = 200;
arr[300] = 300;
for(var i = 0; i < arr.length; i++) {
    count += 1;
    console.log(i + '--' + arr[i]);
}
console.log(count);
// count 1000  轮回了这么屡次
// 打印出100 200 300 和一堆的undefined

so :
for…in 只会遍历对象中存在的实体,而for 轮回则会遍历 1000 次。假如你的营业恰好有如许的须要,那末适宜的运用for..in遍历数组将会发挥更巧妙的作用。
然则平常来看,如许用到的时机很少,上面的指定索引是一种,别的你运用了delete去删除数组元素以后(非特别,不要用delete去删除),也是能够如许遍历的:

var arr = [1,2,3];
delete arr[1]
for(var i in arr) {
    console.log(i + '--' + arr[i]);
}
VM246:4 0--1
VM246:4 2--3

然则,平常状况谁许可你用delete去删除数组元素呢。关于数组能够参考我的另一篇对数组详解的文章。

forEach轮回

for…in是为遍历对象属性设想的,固然也能够遍历数组。厥后,ES5也为素组设想了一个forEach要领来遍历。
forEach要领为数组中含有有效值的每一项实行一次 callback。callback有三个参数:
callback(数组当前项的值,数组当前项的索引,数组对象自身)

var arr = [1,2,3,4,5,'xzavier',7];
arr.forEach(function (value, index, arr) {
    console.log(value);
});
// 1 2 3 4 5 xzavier 7

注重:

1.运用forEach轮回的时刻,须要注重,forEach 遍历的局限在第一次挪用 callback 前就会肯定。挪用forEach 后增加到数组中的项不会被 callback 接见到。

在数组背面增加:

var arr = [1,2,3,4,5,'xzavier',7];
arr.forEach(function (value, index, arr) {
    if (value == 3) {
        arr.push(8);
    }
    console.log(value);
});
// 1 2 3 4 5 xzavier 7

在数组前面增加:

var arr = [1,2,3,4,5,'xzavier',7];
arr.forEach(function (value, index, arr) {
    if (value == 3) {
        arr.unshift(0);
    }
    console.log(index + '--' +value);
});
VM316:6 0--1
VM316:6 1--2
VM316:6 2--3
VM316:6 3--3
VM316:6 4--3
VM316:6 5--3
VM316:6 6--3

什么状况?因为你在数组前增加1项后,遍历下一项的值,发现值照样3,所以背面的遍历一直都走到if语句里了。
然则数组的遍历局限是不可变的。

2.假如已存在的值被转变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。

var arr = [1,2,3,4,5,'xzavier',7];
arr.forEach(function (value, index, arr) {
    arr[5] = 'xx';
    console.log(value);
});

3.已删除的项不会被遍历到(运用delete要领等状况,也许直接置为undefined)。

var arr = [1,2,3,4,5,'xzavier',7];
arr.forEach(function (value, index, arr) {
    if (value == 3) {
        delete arr[5];;
    }
    console.log(value);
});
// 1 2 3 4 5 7

然则运用别的要领删除就就会失足哦:

var arr = [1,2,3,4,5,'xzavier',7];
arr.forEach(function (value, index, arr) {
    if (value == 3) {
        arr.shift(0);
    }
    console.log(value);
});
// 1 2 3 5 xzavier 7   

第四个值不在了,因为forEach记住了最最先的length,然则shift却转变了length。其他的要领同理。
而delete并没有把数组项删掉,只是把值置为了undefined,所以length未变。

4.不能运用break或continue中缀或跳出轮回,报错。不过能够return false 跳出当前轮回,到达肯定结果:

var arr = [1,2,3,4,5,'xzavier',7];
arr.forEach(function (value, index, arr) {
    if (value == 3) {
        return false;
    }
    console.log(value);
});
// 1 2 4 5 xzavier 7

for…of语句

for…in轮回,只能取得对象的键名,不能直接猎取键值,而且遍历数组不友好。
forEach中又不能运用break语句中缀轮回,也不能运用return语句返回到外层函数。

ES6供应for…of轮回,许可遍历取得键值。假如要经由过程for…of轮回,猎取数组的索引,能够借助数组实例的entries要领和keys要领。它还能够准确相应break、continue和return语句。for-of 还能够遍历 Map 和 Set (ES6 中新增数据鸠合范例),以及其他可迭代对象。

var arr = ['a', 'b', 'c', 'd'];   
for (let i in arr) {
      console.log(i); // 0 1 2 3
}
for (let i of arr) {
      console.log(i); // a b c d
}
for (var i of arr) {
    if(i == 'c'){
        break;
    }
      console.log(i); // a b
}

let str = 'xzavier';
for (let i of str) {
      console.log(i); // x z a v i e r
}

break和continue语句

break 和continue 语句用于在轮回中精确地掌握代码的实行。个中,break 语句会马上退出轮回,强迫继承实行轮回体背面的语句。而continue 语句退出当前轮回,继承背面的轮回。

for (var i = 1; i <= 10; i++) {
    if (i == 5) break; //假如i即是5,就退出轮回
    console.log(i); //1 2 3 4
}
for (var i = 1; i <= 10; i++) {
    if (i == 5) continue; //假如i即是5,就退出当前轮回
    console.log(i); // 1 2 3 4 6 7 8 9 10
}

标记跳转

我们顺序员应当都晓得有个字段叫:goto,能够让你的顺序跳到指定的处所实行。因为goto遭到非议太多,运用这类编码情势会使你的代码难以明白和保护,基本不发起运用,假如JavaScript也有goto语句,那末在上面continue的基础上我们还能够让顺序的实行跳转到指定的代码中的谁人位置。不过,幸亏,JavaScript没有这个标识,所以,我们的天下很欢乐。不过,你如果想如许做,我们照样有相似的完成的:

xzavier: for (var i = 1; i <= 10; i++) {
    if (i == 5) {
        continue xzavier; // 一层轮回,与continue无异
    }
    console.log(i);
}
// 1 2 3 4 6 7 8 9 10

在多层轮回的时刻作用就区分出来了:

xzavier: for (var i = 1; i <= 10; i++) {
    for( var j = 1; j <= 10; j++) {
        if (i == j) {
            continue xzavier;
        }
        console.log(i + '>' + j);
    }
}
VM123:6 2>1
VM123:6 3>1
VM123:6 3>2
VM123:6 4>1
VM123:6 4>2
VM123:6 4>3
VM123:6 5>1
VM123:6 5>2
VM123:6 5>3
VM123:6 5>4
VM123:6 6>1
VM123:6 6>2
VM123:6 6>3
VM123:6 6>4
VM123:6 6>5
VM123:6 7>1
VM123:6 7>2
VM123:6 7>3
VM123:6 7>4
VM123:6 7>5
VM123:6 7>6
VM123:6 8>1
VM123:6 8>2
VM123:6 8>3
VM123:6 8>4
VM123:6 8>5
VM123:6 8>6
VM123:6 8>7
VM123:6 9>1
VM123:6 9>2
VM123:6 9>3
VM123:6 9>4
VM123:6 9>5
VM123:6 9>6
VM123:6 9>7
VM123:6 9>8
VM123:6 10>1
VM123:6 10>2
VM123:6 10>3
VM123:6 10>4
VM123:6 10>5
VM123:6 10>6
VM123:6 10>7
VM123:6 10>8
VM123:6 10>9

continue xzavier示意跳到标记为xzavier的轮回,并继承下一次迭代。假如不运用这个标识符的话只会在内层轮回中跳出当次轮回并继承下一次轮回。

固然,我们也不发起运用如许的写法,并没有那末好用。举例很随便,并非要完成一个如许的营业。假如要完成相似的功用,用continue换个写法就OK了。这里重要申明下我们照样有如许的写法存在的,用不用看你心境,看你的场景,以及你的团队同不同意。

with语句

with语句的作用是将代码的作用域设置到一个特定的对象中。当代码运转到with语句时,实行上下文的作用域链暂时被转变了。一个新的可变对象被建立,它包含了参数指定的对象的一切属性。这个对象将被推入作用域链的头部,这意味着函数的一切局部变量如今处于第二个作用域链对象中,因而接见价值更高了。假如,再挖深一点,JavaScript 引擎在编译阶段碰到with字段都不能好好的干活儿了,因为它不晓得这个with最后会怎样转变作用域,本想把变量作用域都安设好都不行了。所以,平常不发起运用。

    原文作者:xzavier
    原文地址: https://segmentfault.com/a/1190000006669505
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞