与其他的编程语言一样,javascript也存在三种基础的程序执行结构。分别是顺序结构、选择结构、循环结构。我们依次来了解一下
顺序结构
执行一个js文件,解析器默认会从第一行依次往下执行,分别执行行为、与定义。如下
let a = 10;
let sqrt = x => x*x;
console.log(sqer(a)); //输出100;
以上代码的执行顺序为 1 2 3 2 3,其中第一次执行的二三行分别是定义函数与调用函数,第二次的二三行分别是执行函数、获取返回值。程序执行固然是顺序的,即使由函数这种代码组织方式改变了执行次序,我们也说它们是顺序的。另外一点需要注意的是,javascript中没用所谓的主函数它的程序入口点、取决于执行环境对他的解释。
在浏览器环境中,程序的执行顺序取决于script标签出现在html文件中的顺序,浏览器会一边构造dom,一边执行javascript。
而在服务器环境中(nodejs),程序的入口点在于在于使用node命令后紧跟的那个js文件名,那个文件我们称其为启动文件,解释器会从这个文件的第一行开始解释执行。再由模块系统引入其他文件交由解释器去执行,这样就形成了一个完整的服务器项目。
选择结构
// js中的选择结构
let a = true;
if(a)
console.log('a is true');
else
console.log('a is false');
console.log( a?'a is true':'a is false' );
let str = a?'a is true':'a is false';
console.log(str);
let code = 200;
switch(code){
case 200: console.log('服务器响应成功'); break;
case 500: console.log('服务器错误');break;
default: console.log('服务器发生未知错误');
}
最常见的选择结构是if、else,表示的含义及"如果...否则..."。其判断的条件是括号内的表达式。顺带一提,表达式可以理解称任何一段合法的javascript代码,如一个已经声明的变量名、一个函数调用、一个常量、一个对象的某个成员变量或某个成员方法的调用。
if语句会将括号中的条件最终转成逻辑值后进行真假判断,即调用Boolean(表达式)。笔者使用谷歌浏览器做了以下代码的测试。
let testlist = [
Boolean(null), //false
Boolean(undefined), //false
Boolean(""), //false
Boolean(0), //false
Boolean({}), //true
Boolean(-1), //true
Boolean(1), //true
Boolean("true"), //true
Boolean("false"), //true
];
testlist.map( item => console.log(item) );
我们暂时将Boolean(表达式)所返回的值称为该表达式的逻辑含义,则在javascript中 null、undefined、空字符串、0的逻辑含义均为false。因此javascript中常常使用一种模式去确保接线来的操作可执行。即判断变量或对象成员的存在性。
let obj = {
x : 1,
};
let sqrt = x => x*x;
if( obj.x )
console.log(sqrt(obj.x));
// 当然,由于x的值为0也是合法的,因此上面的代码改为以下的形式更为合适
if( obj.x || obj.x == 0 )
console.log(sqrt(obj.x));
if、elese也存在如下方式的嵌套使用。
let a = true;
let b = false;
if(b)
console.log('b is true');
else if( a )
console.log('a is true');
else
console.log('all false');
if、else中,若需要执行的代码不只一行,则需使用代码块的方式书写。
let a = true;
let b = false;
if(b){
console.log('b is true');
console.log('其他代码');
}
else if( a )
console.log('a is true');
else{
console.log('all false');
console.log('其他代码');
}
选择结构中存在简写的三元表达式,其使用方法为[条件表达式]?[条件为真时执行的表达式或代码块]:[条件为假时执行的表达式或代码块]。如下代码示例。
let a = 1+1 == 2 ? true:false;
console.log('1+1 == 2 这个表达式的返回值为:'+a); // true
同样也可以使用代码块。
let a = true;
a?console.log('a == true'):{
console.log('a != true');
console.log('其他代码');
};
三元表达式也是可以嵌套书写的,其结合顺序为从右至左。
let a = 1+1 == 2 ? true : 1+2 == 3? true:false; // a = true
//以上表达式等价于
let a = 1+1 == 2 ? true : ( 1+2 == 3 ? true:false );
console.log(a); //输出true
三元表达式通常用于书写单行逻辑,在赋值语句中尤为常见,经常用于处理"若条件成立则变量赋一个值,不成立赋另一个值"的需求。
另外一种常见的选择结构是swatch语句,它是一种多重选择语句,存在的意义是替代多重if else嵌套。
其使用方式为switch后紧跟一对括号,括号中填写需要判断的表达式,而后紧跟一个花括号,其中书写一个一个的case后跟一个合法的原始类型常量,后跟冒号,之后书写一系列的语句,直到书写完成,使用break跳出选择。
let a = 'add';
switch(a){
case 'add':console.log('add');break;
case 'exit':console.log('exit');break;
default:console.log('命令非法'); //default关键字不是必须的,用于处理未知条件表达式返回
}
值得注意的是break的使用仅用于跳出当前选择,且switch的选择会逐一匹配合法的原始类型常量,匹配正确后会进入执行,直到遇见break语句,或整个花括号结束。如下代码。
let a = 'a';
switch(a){
case 'a':
case 'b':
case 'c':
// 字母省略
case 'x':console.log('这是一个小写字母');break;
case '+':
case '-':
case '*':
case '/': console.log('这是四则运算符');break;
default: console.log('不知道是什么');
}
循环结构
javascript中循环的方式有很多种,可将其分为两种,一种为非递归循环,一种为递归循环。
其中非递归循环使用三种关键字for、while、do while。
for循环通常用来处理一些已知循环次数的需求,如下。
for( let i = 0;i<1000;i++ )
console.log('love pp');
// 以上代码输出1000次字符串 "love pp"
其较为常见的一种结构是for([初始化语句][条件判断语句][步长语句])。
当首次执行时,解释器会执行循环的初始化语句,后判断条件判断语句的返回值是否为true,若为true则进入循环执行代码,若不是则退出循环继续按顺序结构执行其他代码。当一次内部循环完成时,解释器会自动执行一次步长语句。
for循环还存在以下两种针对可迭代对象的结构,最常见的可迭代对象便是数组了。
let arr = ['a','b','c'];
for( let item of arr )
console.log(item); //依次输出 a b c
for( let i in arr )
console.log( arr[i] ); //依次输出 a b c
可见for语句接受一个表达式作为参数,分别为of 和 in。当使用of时,每次循环取出的迭代对象的具体元素,而使用in的时候取出的是元素下标(或者叫当前循环的次数)。
while循环通常用于处理一些未知循环次数的需求,如下。
let a = 1;
while( a <= 100 ) a++;
console.log(a); //输出101
而do while则是提前执行循环体的手段。
let a = 1;
do{
console.log('一些代码');
console.log('一些代码');
console.log('一些代码');
a++;
}while(a<100);
可见while循环更加容易造成死循环,即在循环体内部忘记改变条件判断中关联的量。
循环中的递归循环主要通过函数实现,即函数自身调用自身,如下一段代码。
let f = x => x == 1 ? 1 : x == 2 ? 2 : f(x-1) + f(x-2);
console.log(f(20)); //求前20个斐波那契数列的和 10946
除了非递归与递归循环,es6的数组对象中也存在大量的迭代函数可以达到循环的目的,由此可见循环的大多数场景是遍历某个可迭代对象。
其中最常见的有两种方法,map、reduce。
这节课只介绍map方法,关于其他数组上的迭代方法请查询相关文档自行了解。
['a','b','c'].map( item => console.log(item) ); //依次输出
// map 方法接受一个函数作为参数,这个函数的完整接受参数有三个,如下
['a','b','c'].map( (item,index,arr) => console.log(item,index,arr) ); //输出结果如下
/*
a 0 ['a','b','c']
b 1 ['a','b','c']
c 2 ['a','b','c']
*/