本系列内容由ZouStrong整理收录
整理自《JavaScript权威指南(第六版)》,《JavaScript高级程序设计(第三版)》
表达式和语句有很多共同处,不确切的说
表达式是JavaScript中的一个短语,JavaScript解释器会将其计算出一个结果
基本表达式是表达式的最小单位,它不再包含其它表达式
基本表达式通常是直接量、关键字、常量和变量
直接量
1.23 //数字直接量
"strong" //字符串直接量
/strong/ //正则表达式直接量
保留字
true //返回true
null //null
this //返回"当前对象"
变量
sum //返回变量sum中保存的值
undefined //undefined是一个全局变量,和null不同,它不是关键字
对象和数组的初始化表达式实际上就是对象直接量和数组直接量,但是和布尔或者数字直接量不同,它们所包含的成员或者元素都是子表达式
[] //空数组
[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下会报错
函数定义表达式定义一个JavaScript函数
var a = function(){
};
属性访问表达式返回一个对象属性或者数组元素的值
属性访问有两种语法
expression.identifier
expression[expression]
对象的方括号语法不常用
但是如果属性名称是一个关键字或者保留字、或者包含空格和标点符号、或者是一个数字,则必须使用方括号语法
当属姓名是通过计算得出的值而不是固定的值得时候(如变量),也必须使用方括号语法
调用表达式是一种调用函数或者方法的语法表示
它以一个函数表达式开始,后跟一对圆括号和参数列表
a()
Math.random()
对象创建表达式创建一个对象并调用一个函数(构造函数)初始化新对象的属性
new Object()
new Array()
和对象初始化表达式({})一样,首先创建一个空对象,然后传入指定的参数来调用这个函数,并将新对象通过this值传递给该函数
然后,构造函数使用this来初始化这个新创建对象的属性
构造函数不用返回一个值,这个新创建并被初始化的对象就是整个对象创建表达式的值,如果构造函数确实返回了一个对象值,那么这个对象就是整个对象创建表达式的值,新创建的对象被废弃
算术表达式就是常规的算术运算
1+1
100/3
关系表达式就是比较运算
a>b
逻辑表达式就是逻辑运算
a&&b
赋值表达式就是赋值运算
a=4;
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()会将其字符串当成顶层的全局代码来执行,也就是说作用域仍然属于全局作用域,无法读写和定义局部变量和函数
var geval = eval;
var x =1;
function test(){
var x=2;
geval("var x=3");
}
test(); //最终全局变量x被修改
这是一种非常有用的特性,允许我们执行哪些对上下文没有任何依赖的全局代码段
严格模式下,不能将eval赋值给其它变量
表达式是短语,返回一个计算出的值
语句是整句或者命令,用分号(;)结尾,用来使某事发生
JavaScript程序就是一系列可执行语句的集合
具有副作用的表达式是JavaScript中最简单的语句,赋值语句就是表达式语句
a = "hello";
自增自减运算符都和表达式语句有关
count++;
delete运算符的一个重要作用就是删除对象的属性,所以一般做为语句使用
delete o.x;
函数调用是表达式语句的另一个大类
getData();
JavaScript中还可以将多条语句联合起来,形成一条复合语句,只需用花括号将多条语句括起来即可
{
x=Math.PI;
y=Math.cos(x);
}
这个语句块就会作为一条语句来执行,关于语句块,有几点需要注意
空语句则是包含0条语句的语句,执行空语句时不会进行任何动作
;
注意,在for循环、while循环、if语句的右圆括号后的分号很不起眼,但是却可能造成致命BUG
if(x>0);
{
alert("x大于0"); //本句总是执行
}
所以将开头的大括号紧跟圆括号还可以避免此类BUG
声明语句由var或者function关键字组成
var语句用来声明一个或多个变量
var a; //声明变量,自动取得undefined
var b=3; //声明变量,并带有初始化表达式
var a=3,b=4; //同时声明多个变量
如果var语句出现在函数体内,那么它定义的就是一个局部变量,其作用域仅限于当前函数
如果var语句出现在顶层代码中,那么它生命的是一个全局变量,在整个JavaScript程序中都是可见的,而省略var,则始终会创建全局变量
虽然使用var声明的全局变量会成为全局对象的属性,但是和其它全局对象属性不同的是,var声明的变量是无法通过delete删除的
使用var声明的变量会提前到当前作用域的前端,但是初始化的操作还是在原位置进行
关键字function用来声明一个函数
function test(arg1,arg2){
}
在声明函数时,并不会执行函数体内的语句(此时,即使有错误也不会报),只有在调用函数时,才会执行
同使用var一样,函数声明创建的变量也是无法通过delete删除的
同变量声明一样,函数声明也会提升到当前作用域的顶端(函数名和函数体都会提前)
但是函数定义的函数体不会提前(那就完全相当于变量声明了)
条件语句通过判断表达式的值来决定是否执行某些语句
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{
}
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时,应注释清楚)
条件语句是分支,而循环语句就是回路,可以让指定代码重复执行
while (expression){ //前测试循环(迭代)语句
}
只要expression的值为真,就会一直执行循环体内的代码,直到expression的值为假值
while(true){
//无法结束的死循环
}
while循环体内的代码可能会永远不会执行
do { //后测试循环(迭代)语句
} while (expression);
do/while循环体内的代码至少执行一次,然后才去判断expression,此外,do/while循环是要以分号结尾的
for (initialize; test; increment) {
}
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++;
}
for-in语句是一种精准的循环(迭代)语句,用来枚举对象的属性
for ( var name in someObject){
//每次循环都将该对象中的一个属性名赋值给name变量
alert(name);
alert(someObject[name]);
}
对于数组,赋给name的是索引值
只有可枚举属性才会被返回,并且顺序随机
跳转语句使得程序的执行从一个位置跳转到另一个位置
使用label语句可以在代码中添加标签,label语句的出现,不影响程序的正常运行,但通过给语句添加标签,就可以在程序的任何地方通过标签名引用该语句
通常标签由break或者continue语句引用,也就是表明了跳转的位置
label语句通常和循环配合使用,且一般用于多层嵌套循环,以便跳出到指定位置
start: for (var i=0; i < count; i++) {
}
break语句只出现在循环和switch中
单独使用break会立即结束最内层的循环或switch语句
break;
此外,break关键字后面可以跟随一个标签(由label语句指定),用于立即结束这个标签做标识的语句块
break labelName;
continue语句只出现在循环中,会立即跳过本次循环,继续执行下一次循环
continue语句同样可以带有标签
return语句只出现在函数内部,用于返回值并中止函数的执行(return语句之后的代码不会执行),进而执行函数外部的后续代码
异常(exception)是指当发生了某种异常情况或者错误时产生的一个信号,抛出异常是指用明确的信号通知我们发生了某种异常情况或者错误,捕获异常是指处理这个信号,采用必要手段使程序从异常中恢复
当产生运行时错误或者使用throw语句时,就会显式的抛出异常,而使用try/catch/finally语句就可以捕获异常
throw expression;
expression值可以是任意类型的,可以是一个代表错误码的数字,或者包含错误信息的字符串,但我们通常采用Error类型或者子类型
Error对象通常会有以下属性
例:
function test(num){
if(num<0){
throw new Error('不能传递负数');
}
}
异常抛出后,JavaScript会立即停止后续代码的执行,并跳转至最近的异常处理程序(catch语句),如果没有找到异常处理程序,则JavaScript将把异常当做程序错误,并告知用户(通常是浏览器控制台)
try/catch/finally语句是JavaScript的异常处理机制
catch和finally都是可选的,但至少需要其中一个
try {
//我们希望这里的代码从头执行到尾,不产生任何问题
//但有时会抛出异常,要么是throw语句直接抛出
//要么是通过调用一个方法间接抛出异常
}catch(e){
//当且仅当try语句块抛出了异常,才会执行这里的代码
//这里通过局部变量e获得对Error对象或者抛出的其它值的引用
//在这里处理异常或者忽略异常,甚至再抛出异常
}finally{
//这里的代码总是执行
}
with语句用于临时扩展作用域链
with (object){
}
这条语句将object添加到作用域链的顶端,然后执行代码块,最后把作用域链恢复到原始状态
在with语句的代码块内部,每个变量首先被认为是一个局部变量,而如果在局部环境中找不到该变量的定义,就会查询object对象中是否有同名的属性。如果发现了同名属性,则以该对象属性的值作为变量的值
由于with会将对象添加到当前作用域链,增加了变量查找的时间,所以使用with语句会导致性能下降,同时也会给调试代码造成困难,因此不建议使用with语句,在严格模式下,使用with语句会报错
debugger语句用于调试程序,产生一个断点,JavaScript代码的执行会在断点处停止
debugger;
debugger不会打开控制台,只有控制台已经打开的时候,debugger语句才会生效、才会去产生断点