3.核心JavaScript笔记:表达式、语句

本系列内容由ZouStrong整理收录

整理自《JavaScript权威指南(第六版)》,《JavaScript高级程序设计(第三版)》

表达式和语句有很多共同处,不确切的说

  • 表达式仅仅计算出一个值(有返回值)但并不做任何操作,它并不改变程序的运行状态
  • 语句并不包含一个值(没有返回值,或者说,它返回的值我们并不关心)但它改变程序的运行状态

一. 表达式

表达式是JavaScript中的一个短语,JavaScript解释器会将其计算出一个结果

1. 基本表达式

基本表达式是表达式的最小单位,它不再包含其它表达式

基本表达式通常是直接量、关键字、常量和变量

直接量

 1.23          //数字直接量
"strong"	    //字符串直接量
/strong/	    //正则表达式直接量

保留字

true   //返回true
null  //null
this   //返回"当前对象"

变量

sum  //返回变量sum中保存的值
undefined    //undefined是一个全局变量,和null不同,它不是关键字

2. 对象和数组的初始化表达式

对象和数组的初始化表达式实际上就是对象直接量和数组直接量,但是和布尔或者数字直接量不同,它们所包含的成员或者元素都是子表达式

[]            //空数组
[1,2+1]        //拥有两个元素的数组

数组初始化表达式中的元素也可以是数组初始化表达式,可以嵌套

[[1],[2,3],[3,4,5]]   //拥有三个元素的数组,数组元素仍是数组

对数组初始化表达式进行求值得时候,其中的子表达式也都会各自计算一次,因此

数组初始化表达式每次计算的值有可能是不同的

var i =1;
a=[i++];   //1
a=[i++];  //2

数组直接量中的元素之间的逗号可以省略,省略的值自动获得undefined(其实,虽然访问该位置会返回undefined,但是实际上创建的是稀疏数组,该位置是没有值的)

[1,,,,3]

数组直接量中的最后一个元素后面最好不要添加逗号,因为在低版本IE下,会认为逗号后面是有元素的

[1,2,]  //标准浏览器下返回长度2,低版本IE下返回长度3

对象初始化表达式也类似

{}       	 //空对象
{x:1,y:2}    //拥有两个元素的对象
{x:{y:1}}     //嵌套的对象初始化表达式

同样,对象初始化表达式进行求值得时候,其中的子表达式也都会各自计算一次

对象直接量中的最后一个元素后面一定不要添加逗号,因为在低版本IE下会报错

3. 函数定义表达式

函数定义表达式定义一个JavaScript函数

var a = function(){
};

4. 属性访问表达式

属性访问表达式返回一个对象属性或者数组元素的值

属性访问有两种语法

expression.identifier
expression[expression]

对象的方括号语法不常用

但是如果属性名称是一个关键字或者保留字、或者包含空格和标点符号、或者是一个数字,则必须使用方括号语法

当属姓名是通过计算得出的值而不是固定的值得时候(如变量),也必须使用方括号语法

5. 调用表达式

调用表达式是一种调用函数或者方法的语法表示

它以一个函数表达式开始,后跟一对圆括号和参数列表

a()
Math.random()

6. 对象创建表达式

对象创建表达式创建一个对象并调用一个函数(构造函数)初始化新对象的属性

new Object()
new Array()

和对象初始化表达式({})一样,首先创建一个空对象,然后传入指定的参数来调用这个函数,并将新对象通过this值传递给该函数

然后,构造函数使用this来初始化这个新创建对象的属性

构造函数不用返回一个值,这个新创建并被初始化的对象就是整个对象创建表达式的值,如果构造函数确实返回了一个对象值,那么这个对象就是整个对象创建表达式的值,新创建的对象被废弃

7. 算术表达式

算术表达式就是常规的算术运算

1+1
100/3

8. 关系表达式

关系表达式就是比较运算

a>b

9. 逻辑表达式

逻辑表达式就是逻辑运算

a&&b

10. 赋值表达式

赋值表达式就是赋值运算

a=4;

11. 表达式计算

JavaScript可以直接解释运行由JavaScript源代码组成的字符串,并返回一个值,通过全局函数eval()来完成

eval("2+3");    //5

动态判断源代码中的字符串是一个强大的功能,但是几乎没有必要在实际中使用

eval()只接受一个参数,即一个字符串,然后把这段字符串当做JavaScript代码来解析,并返回字符串中最后一个表达式或者语句的值,否则返回undefined

关于eval(),要知道的就是,传递给eval()的代码并没有自己的作用域,其作用域仍然属于其外部执行环境

eval("var a=3");
a   //3

eval()中的字符串执行时的上下文和调用函数的上下文环境是一样的,这使得不能将其作为函数的一部分来运行

var a = function(x){
	eval(x);
}
a("return 1;")    //报错,return只能出现在函数中

全局eval

eval()会改变局部变量

但当通过别名调用时,eval()会将其字符串当成顶层的全局代码来执行,也就是说作用域仍然属于全局作用域,无法读写和定义局部变量和函数

var geval  = eval;
var x =1;	
function test(){
	var x=2;		
	geval("var x=3");	
}
test();   //最终全局变量x被修改

这是一种非常有用的特性,允许我们执行哪些对上下文没有任何依赖的全局代码段

严格模式下,不能将eval赋值给其它变量

二. 语句

表达式是短语,返回一个计算出的值

语句是整句或者命令,用分号(;)结尾,用来使某事发生

JavaScript程序就是一系列可执行语句的集合

1. 表达式语句

具有副作用的表达式是JavaScript中最简单的语句,赋值语句就是表达式语句

a = "hello";

自增自减运算符都和表达式语句有关

count++;

delete运算符的一个重要作用就是删除对象的属性,所以一般做为语句使用

delete o.x;

函数调用是表达式语句的另一个大类

getData();

2. 复合语句和空语句

JavaScript中还可以将多条语句联合起来,形成一条复合语句,只需用花括号将多条语句括起来即可

{
	x=Math.PI;
	y=Math.cos(x);
}

这个语句块就会作为一条语句来执行,关于语句块,有几点需要注意

  • 语句块的结尾不需要分号,但块中的每一条语句必须以分号结束
  • 语句块中的行都有缩进,虽不是必须的,但能够增强可读性
  • JavaScript没有块级作用域,语句块中的变量并不是语句块私有的(ECMAScript 6新增了块级作用域)

空语句则是包含0条语句的语句,执行空语句时不会进行任何动作

;

注意,在for循环、while循环、if语句的右圆括号后的分号很不起眼,但是却可能造成致命BUG

if(x>0);
{
	alert("x大于0");        //本句总是执行
}

所以将开头的大括号紧跟圆括号还可以避免此类BUG

3. 声明语句

声明语句由var或者function关键字组成

1)var

var语句用来声明一个或多个变量

var a;      //声明变量,自动取得undefined
var b=3; //声明变量,并带有初始化表达式
var a=3,b=4;  //同时声明多个变量

如果var语句出现在函数体内,那么它定义的就是一个局部变量,其作用域仅限于当前函数

如果var语句出现在顶层代码中,那么它生命的是一个全局变量,在整个JavaScript程序中都是可见的,而省略var,则始终会创建全局变量

虽然使用var声明的全局变量会成为全局对象的属性,但是和其它全局对象属性不同的是,var声明的变量是无法通过delete删除的

变量声明提前

使用var声明的变量会提前到当前作用域的前端,但是初始化的操作还是在原位置进行

2)function

关键字function用来声明一个函数

function test(arg1,arg2){
}

在声明函数时,并不会执行函数体内的语句(此时,即使有错误也不会报),只有在调用函数时,才会执行

同使用var一样,函数声明创建的变量也是无法通过delete删除的

函数声明提升

同变量声明一样,函数声明也会提升到当前作用域的顶端(函数名和函数体都会提前)

但是函数定义的函数体不会提前(那就完全相当于变量声明了)

4. 条件语句

条件语句通过判断表达式的值来决定是否执行某些语句

1)if语句,else语句,else if语句

if( expression ){  //条件语句
}

expression可以是任意表达式;而且对这个表达式求值的结果不一定是布尔值,如果不是布尔值,会默认调用Boolean()转换函数来转换

还可以使用else从句,当expression的值被转换成false的时候执行else中的逻辑

if( expression ){
}else{
}

JavaScript语法规定,if()或者else之后必须跟随至少一条语句,且只有一条语句时,大括号是可以省略的(为了最佳实践,永远不要省略)

使用if..else..可以选择两条分支中的一条,当有多条分支的时候,使用else if语句(并不是真正的语句,而是多条if...else...语句连在一起时的惯用写法)

if( expression ){
}else if( expression2 ){
}else{
}

2)switch语句

if语句在程序执行过程中创建多条分支,然而当所有分支都依赖于同一个表达式的值时,使用if..else..语句并不是最佳解决方案

switch语句应运而生(switch语句只能判断是否等于)

switch (expression) { 
	case value1: 
	statement;
	break; 
	……...
	default: 
	statement;
}

即相当于如下语句

if(express == value1){
}else if( express == value2}{
}else{
}

在switch语句中可使用任何数据类型,其次,每个case的值不一定是常量,可以是变量,甚至是表达式

switch语句在比较值时使用的是全等操作符,因此不会发生类型转换

break语句可以避免同时执行多个case代码的情况,如果没有break,则会从最先的匹配项开始一直执行到末尾或者遇到一个break为止(有意忽略break时,应注释清楚)

5. 循环(迭代、遍历)语句

条件语句是分支,而循环语句就是回路,可以让指定代码重复执行

1)while语句

while (expression){   //前测试循环(迭代)语句
}

只要expression的值为真,就会一直执行循环体内的代码,直到expression的值为假值

while(true){
	//无法结束的死循环
}

while循环体内的代码可能会永远不会执行

) do/while语句

do {               //后测试循环(迭代)语句
} while (expression);

do/while循环体内的代码至少执行一次,然后才去判断expression,此外,do/while循环是要以分号结尾的

3)for语句

for (initialize; test; increment) {
}

for语句也属于前测试循环(迭代)语句,但它具有在循环之前初始化变量和定义循环后要执行的代码的能力

  • 初始化只在循环开始之前执行一次(不属于循环内),所以完全可以在外部定义
  • 只有表达式返回true的情况下才会进入for循环,因此也有可能不会执行循环内的代码
  • 只有执行了循环体,才会对循环后表达式求值

例:

for (var i=0 ; i<5 ; i++) {
} 
//相当于
var i=0
while (i<5){
	i++;
}

使用while循环做不到的,使用for循环同样也做不到(for循环只是把与循环有关的代码集中在了一个位置)

此外,for语句中的初始化表达式、控制表达式和循环后表达式都是可选的。将这三个表达式全部省略,就会创建一个无限循环

for (;;) {   // 无限循环
}

而只给出控制表达式实际上就把for循环转换成了while循环

var i=0 , count = 10; 
for (;i < count;){
	i++; 
}

4)for-in语句

for-in语句是一种精准的循环(迭代)语句,用来枚举对象的属性

for ( var name in someObject){
	//每次循环都将该对象中的一个属性名赋值给name变量
	alert(name);
	alert(someObject[name]);
}	

对于数组,赋给name的是索引值

只有可枚举属性才会被返回,并且顺序随机

6. 跳转语句

跳转语句使得程序的执行从一个位置跳转到另一个位置

1) label语句

使用label语句可以在代码中添加标签,label语句的出现,不影响程序的正常运行,但通过给语句添加标签,就可以在程序的任何地方通过标签名引用该语句

通常标签由break或者continue语句引用,也就是表明了跳转的位置

label语句通常和循环配合使用,且一般用于多层嵌套循环,以便跳出到指定位置

start: for (var i=0; i < count; i++) { 
} 

2) break语句

break语句出现在循环和switch中

单独使用break会立即结束最内层的循环或switch语句

break;

此外,break关键字后面可以跟随一个标签(由label语句指定),用于立即结束这个标签做标识的语句块

break labelName;

3)continue语句

continue语句出现在循环中,会立即跳过本次循环,继续执行下一次循环

continue语句同样可以带有标签

4)return语句

return语句出现在函数内部,用于返回值并中止函数的执行(return语句之后的代码不会执行),进而执行函数外部的后续代码

5)throw语句

异常(exception)是指当发生了某种异常情况或者错误时产生的一个信号,抛出异常是指用明确的信号通知我们发生了某种异常情况或者错误,捕获异常是指处理这个信号,采用必要手段使程序从异常中恢复

当产生运行时错误或者使用throw语句时,就会显式的抛出异常,而使用try/catch/finally语句就可以捕获异常

throw expression;

expression值可以是任意类型的,可以是一个代表错误码的数字,或者包含错误信息的字符串,但我们通常采用Error类型或者子类型

Error对象通常会有以下属性

  • name属性:错误类型
  • message属性:具体的错误信息(传递给Error构造函数的字符串)

例:

function test(num){
	if(num<0){
		throw new Error('不能传递负数');
	}
}

异常抛出后,JavaScript会立即停止后续代码的执行,并跳转至最近的异常处理程序(catch语句),如果没有找到异常处理程序,则JavaScript将把异常当做程序错误,并告知用户(通常是浏览器控制台)

6)try/catch/finally语句

try/catch/finally语句是JavaScript的异常处理机制

  • try从句定义了需要处理的异常所在的代码块
  • catch从句跟随在try从句之后,当try块内发生异常时,调用catch块内的代码
  • finally从句跟随在catch从句之后,不管try块中是否发生异常,finally块内的代码总是会执行
  • try/catch/finally语句后面的花括号是必须的,即使只有一条语句也不能省略
  • catch和finally都是可选的,但至少需要其中一个

    try {
    //我们希望这里的代码从头执行到尾,不产生任何问题
    //但有时会抛出异常,要么是throw语句直接抛出
    //要么是通过调用一个方法间接抛出异常
    }catch(e){
    //当且仅当try语句块抛出了异常,才会执行这里的代码
    //这里通过局部变量e获得对Error对象或者抛出的其它值的引用
    //在这里处理异常或者忽略异常,甚至再抛出异常
    }finally{
    //这里的代码总是执行
    }

7. 其它语句

1)with语句

with语句用于临时扩展作用域链

with (object){
}

这条语句将object添加到作用域链的顶端,然后执行代码块,最后把作用域链恢复到原始状态

在with语句的代码块内部,每个变量首先被认为是一个局部变量,而如果在局部环境中找不到该变量的定义,就会查询object对象中是否有同名的属性。如果发现了同名属性,则以该对象属性的值作为变量的值

由于with会将对象添加到当前作用域链,增加了变量查找的时间,所以使用with语句会导致性能下降,同时也会给调试代码造成困难,因此不建议使用with语句,在严格模式下,使用with语句会报错

2)degugger语句

debugger语句用于调试程序,产生一个断点,JavaScript代码的执行会在断点处停止

debugger;

debugger不会打开控制台,只有控制台已经打开的时候,debugger语句才会生效、才会去产生断点

你可能感兴趣的:(JavaScript)