,什么是javascript 语句?
表达式javascript短语,语句是javascript整句或命令,javascript语句是分号结尾。表达式计算出一个值,但是语句用来执行某件事发生。
"使某件事发生"的一个方法是计算带有副作用的表达式。诸如赋值和函数调用这些有副作用的表达式,是可以作为单独的语句,这种表达式当做语句的用法称为表达式语句。类似的语句还有声明语句,声明语句来声明新变量或者定义新函数。
js程序无非就是一系列可执行语句的集合,默认情况下,js解析器依照语句的编写顺序依次执行。另一种“使某件事发生”的方法是改变语句的默认执行顺序。javascript中有很多语句和控制结构来改变语句的默认执行顺序
●条件语句,javascript解释器可以根据一个表达式的值来判断是执行还是跳过这些语句,如if语句和switch语句。
●循环语句,可以重复执行语句,如while和for语句
●跳转语句,可以让解释权跳转至整型的其他部分执行,如break,return和throw语句
1.表达式语句
具有副作用的表达式是JavaScript中最简单的语句。
● 赋值语句
eg:greeting='hello'+name;
i *=3; i+=3; i-=3;
● "++"和"--"递增和递减,因为他们包含了隐式的赋值
eg:i++; i++;
● delete运算符的重要作用时候是删除一个对象的属性,所以一般它作为一个语句使用,而不是作为复杂表达式的一部分
delete o.x
● 函数调用是表达式语句的另一个大类,虽然这些客户端函数调用是表达式,但他们对web浏览器造成了一些影响,我们也认为它们是语句。调用一个没有任何副作用的函数是没有任何意义的,除非它是复杂表达式或者赋值一部分,例如,不可能计算一个余弦值,把他丢掉:
alter(greeting);
window.close();
cx=Math.cos(x);
2.复合语句和空语句
2.1什么是复合语句?
javaScipt中还可以将多条语句联合在一起,形成一条复合语句。只须用花括号将多条语句括起来即可,可以成为语句块,eg
{
var x=Math.PI;
var cx=Math.cos(x);
console.log(cx);
}
console.log(cx); //可以访问到
注:
◆语句块的结尾不需要用分号
◆语句块中的语句必须以分号结尾
◆语句块的行都有缩进,这不是必需的,但是整齐的缩进会使代码可读性增强,更容易理解
◆在es5用var声明的变量,在语句块中并不是语句私有的,可以访问到
2.2 空语句
空语句包含0条语句,js执行空语句的时,它显然不会执行任何动作,它的展示方法是
;
如果有特殊目的需要使用空语句,最好在代码中添加注释,这样可以更清楚地说明空语句是有用的
let arr=[1,23,5,6];
for(let i=0;i<arr.length;arr[i++]=0);//使数组的每一个元素初始化0
console.log(arr);//[0, 0, 0, 0]
3声明语句
◆var 和 function 都是声明的语句,它们声明或者定义变量和函数。这些语句定义标识符(变量名和函数名)并给其赋值,这些标识符只可以在程序中任意地方事宜。声明语句本身什么也不做,但它有一个重要的意义,通过创建变量和函数,可以更好组织代码的语义
3.1 var
var语句用来声明一个变量和多个变量,关键字var之后跟随的是要声明的变量的列表,列表的每一个变量都可以带有一个初始值,用于指定它的初始值,eg:
var i;
var j=0;
var p,q;
var x=2,3,y=2.4,z=77,p,q;
var f=function(x){ return x*x ;}
◆var语句出现在函数体内,那么它定义的是一个局部变量,其作用域就是这个函数。如果在顶层代码中使用var语句,它声明的是全局变量,在整个js程序中是可见的,全局变量是全局对象的属性,var声明的全局变量是无法通过delete删除的
◆如果var语句的变量没有指定初始化表达式,那么这个变量的初始值为undefined,变量声明会被提前到脚本或者函数的顶部,但是初始化的操作则还在var语句的位置执行
◆for循环,或者for/in循环也会提前到顶部
3.2 function
关键字function用来定义函数。定义函数的两种写法
◆函数声明
函数声明的语法如下,fucname是声明函数的名称的标识符。函数名之后的圆括号中是参数列表,参数直接用逗号隔开,当调用函数时,这些标识符则指代传入函数的实参,函数体是由js语句组成,语句的数量不限,且用花括号括起来
注:函数体只包含一条语句也得用花括号包起来
function fucname(a,b,c...){
statements
}
函数声明语句通常会出现在javascript代码的顶层,也可以嵌套在其他函数体内。但在嵌套函数时,函数声明只能出现在所嵌套函数的顶部。函数定义不能出现在if语句,while循环或其他任何语句中
◆函数表达式
函数定义表达式定义一个js函数,函数定义表达式可称为‘函数直接量’。
一个典型的函数定义表达式包含关键字function,跟随其后是一对圆括号,括号内是一个以逗号隔开的列表,列表包含0个或者多个标识符(参数名),然后再跟随一个由花括号包裹的js代码段(函数体),函数定义表达式同样可以包含函数名字。
eg:
var square = function(x) { return x*x}
◆尽管函数声明和函数定义表达式所包含的相同的函数名,但是两者仍然不同,函数声明中的函数时一个变量名,变量指向函数对象和通过var声明变量一样,定义函数语句中的函数被显示的“提前”到了脚本或者函数顶部。使用var的变化,只有变量声明提前了,变量初始化的源代码还在原来的位置
aa(1);//1
function aa(num){
console.log(num)
}
b(1);//b is not a function
var b= function(num){
console.log(num)
}
4.条件语句
什么是条件语句?
条件语句是通过判断指定表达式的值来决定执行还是跳过某些语句。这些语句的“决策点”,有时称为“分支”。如果js解释器是按照代码路径执行的,那么条件语句就是这条路径上的分叉点,程序执行到这里时必须选择其中一条路径继续执行
4.1 if / else 语句
if/else语句是一种基本的空值语句,需要计算expression的值,如果这个值是真值,则执行代码块1,如果是假值,否则执行代码块2
语法如下:
if(expression){
statement1
}else{
statement2
}
注:if/else语句里也可以嵌套if/else语句,eg
if (i == j) {
console.log(1)
} else {
console.log(2);
if (num >= 10) {
console.log('这是大于10的数')
} else {
console.log('这里小于10的数')
}
}
4.2 else if
if/else 语句通过来判断一个表达式的计算结果来选择执行两条分支的一条。但是有多条分支的时候,我们可以用 else if ,代码如下:
if (n == 1) {
} else if (n == 2) {
} else if (n == 3) {
} else {
}
等价于
if (n == 1) {
} else {
if (n == 2) {
} else {
if (n == 3) {
} else {
}
}
}
4.3 switch
if语句在程序执行过程创建一条分支,并且可以用else if处理多个分支,然而当所有的分支判断用一个表达式时可以用switch语句,
关键字switch之后紧跟一个圆括号,圆括号里是一个表达式,随后是一对花括号括起来,随后是一个花括号,花括号中可以使用多个由case关键字识别的代码片段,case关键字之后是一个表达式和冒号,冒号之后是一个段执行语句。"default"标签出现在switch的人末尾,当执行switch语句时,以下面的案例为例
1.先计算expression的值
2.然后查找case子语句的表达式是否和expression值相等,这里的相等是恒等于,不存在类型的转换,
3.如果找到匹配的case,那么执行这个case对应的代码块,如果没有找到匹配的case,则执行default。如果每天default标签,switch将跳过所有的代码块
switch (expression) {
case 1:
console.log(1)
break;
case 2:
console.log(2);
break;
case 3:
console.log(2);
break;
default:
console.log('其他');
break;
}
注:在每一个case语句块的结尾处都使用关键字break,break语句可以使解释器跳出switch循环。如果没有break,那么switch语句就会从 与expression值匹配的case标签处的代码处开始执行,依次执行后续的语句,一直带switch结尾处,eg
function getNum(num) {
switch (num) {
case 1:
console.log(1)
case 2:
console.log(2);
case 3:
console.log(3);
default:
console.log('其他');
}
}
getNum(2)//2 ,3, 其他
5.循环
循环语句就是程序路径的一个回路,可以让一段代码重复执行。javascript中有四种循环语句:while,do/while,for和for/in。最常见是对数组的循环
5.1 while
while语句是一种基本的循环语句,它的语法如下
while(expression){
stetement
}
在执行while语句之前,js解析器先计算expression的值,如果他的值是假值,那么程序直接跳过循环体的代码转而执行下程序中的下一条语句,如果是真值,则执行stetement代码
注:while(true){} 则会创建一个四循环
通常来说,我们并不想js反复执行同一操作,在几乎每次循环中,都会执行一个或多个变量随着循环的迭代而改变。正是由于改变了这些变量,因此每次循环表达式的值也不同 。这一点很重要,否则一个初始的值永远是真值,循环也不会结束。
下面的案例所示while循环0-9之间的值
let count=0;
while(count<10){
console.log(count)
count++;
}
5.2 do/while
do/while循环和while循环非常相似,只不过它是在循环的尾部而不是顶部检测表达式,这就意味循环体至少执行一次。语法规则
do{
statement
}while(expression);
eg:
let arr=[1,3,45,6]
function getArr(arr){
let len=arr.length,i=0;
if(len==0){
console.log('空的数组')
}else{
do{
console.log(arr[i])
}while(++i<len)
}
}
getArr(arr)
5.3 for
for语句提供了一种比while循环更加方便的循环控制结构。for对常用的循环模式做了一些简化。大部分的循环都是有计数器变量。在循环之前要初始化这个计数器变量,然后每次循环之前都会检测一下它的值,最后计算器做自增操作,否则就是在循环结束后,下一次判断条件前做修改。在这一类的循环中,计数器三个关键操作,初始化,检测和更新。for语句就将这三步的操作明确声明为循环语法的一部分,各自用一个表达式来表示。for语句语法如下
for(initialize ; test ; increment){
statement
}
等价于
initialize
while(test){
statement;
increment
}
initialize ,test , increment三个表达式之间用分号分隔,它们负责初始化操作,循环条件判断和计数器的更新,换句话说initialize只在循环开始之前执行一次,每次循环会执行test表达式,如果为真值,则执行循环体的statement,最后执行increment表达式,increment表达式一般是‘++’和‘’
注:for循环中三个表达式都可以省略任何一个,但是两个分号必不可少,for(; ;)是一种死循环
eg:
//案例一0-9
for(var count=0;count<10;count++){
// console.log(count)
}
//案例二 9-0
for(var count=10;count>=0;count--){
// console.log(count)
}
//案例三 计算1-100的相加之和
let sum=0;
for(var i=0,j=100;i<=100;i++,j--){
sum+=(i+j)
}
console.log(sum/2)
5.4 for/in
for/in循环用来遍历对象的属性成员,语法如下
for (variable in object){
statement
}
variable通常是一个变量名,也可以是一个产生左值的表达式或者通过var 声明一个变量,总之是一个适用于赋值表达式左侧的值,object是一个表达式,这个表达式计算的结果是一个对象
在执行for/in语句的过程中,js首先计算object表达式,如果表达式是null或undefined,js会跳过循环并执行后续代码。如果表达式是一个而原始值,这个原始值会转为之对应的包装对象。如果object已经是对象。js依次枚举对象的属性来执行循环。然而在每次循环之前,js都会计算variable值,并将属性名(字符串)赋值给他
eg:
let obj={
x:1,
y:2,
z:222
}
for (var p in obj){
console.log(obj[p])
}
注:for/in循环中的variable 的值可以当做赋值表达式的左值,他可以是任意表达式,每次循环都会计算这个表达式,也就是说每次循环它计算的值有可能不同。例如,将一个对象的所有的属性复制到一个数组里,代码如下
let obj={
x:1,
y:2,
z:222
}
let arr=[];i=0;
for (arr[i++] in obj);
console.log(arr)
◆ for/in循环只会遍历可枚举属性,也可以遍历继承对象的可枚举属性
属性枚举的顺序
es规范并没有指定for/in循环安装何种顺序来枚举对象的属性。实际上,主流浏览器厂商的js实现是按照定义的先后顺序来枚举的,谁先定义谁先枚举
6.跳转语句
js跳转语句可以使js的执行从一个位置转到另一个位置。
break语句是跳转到循环或者其他语句结束。
continue语句是终止本次循环的执行开始执行下一次循环的执行
js的语句可以命名,带有标签,break和continue可以标识目标循环或者其他语句标签
return语句让解析器跳出循环体的执行,并提供本次调用的返回值
throw语句触发抛出一个异常,它是与try/catch/finally语句一同使用,这些语句指定了处理异常的代码的逻辑。这是一种复杂的跳转的语句,当抛出一个异常的时候,程序将跳转到一个闭合异常处理程序,这个异常处理程序可以是在同一个函数中或者更高层的调用栈
6.1标签语句
语句是可以添加标签的,标签是由语句标识符合冒号组成,通过给语句定义标签,就可以在程序的任何地方通过标签名引用这条语句,只有在给语句块定义标签时它才更有用,比如循环语句和条件判断语句。通过给循环定义一个标签名,可以在循环体内部使用break和continue来退出循环语句标签,或者直接跳到下一个循环的开始
eg
label : statement;
let i = 0;
mainloop: while (i <= 10) {
if (i == 5) {
break mainloop;
}
console.log(i)
i++;
}
注:
◆标签语句名必须是一个合法的js字符
◆标签的命名空间跟变量或函数的命名空间是不同的,所有可以用同一个标识符来作为标签语句名和变量名或函数名
◆不相互嵌套的情况下可以出现同名的语句标签
◆相互嵌套的情况下,语句标签是不能同名的
6.2 break
单独使用break语句的作用是立即退出最内层的循环或者switch语句,它的语法如下:
break;
◆break 只有出现在 switch和循环这类语句中才是合法的。
◆我们在switch语句的例子中已经见到break语句了。
◆在循环中,无论出于什么原因,只有不想执行整个循环,就可以用break来提前退出。
下面的中循环变迁整个数组元素来查找某个特定的值,当整个数组遍历完成后会正常退出,如果找到了需要查找的数组元素,则使用break直接退出
eg:
for(var i=0;i<a.length;i++){
if(a[i]===target) break;
}
break关键字后面跟随一个语句标签,语法如下
break labelname;
当break和标签一块使用时,程序将跳转到这个标签所标识的语句块结束,或者直接闭合语句块执行,当使用这种形式的break语句时,带标签的语句不应该是循环或者switch语句,因为break可以跳出任何闭合的语句块,这里的语句可以是花括号括起来的一组语句,使用同一个标签来标识这一组语句
当你希望通过break来跳出非就近的循环体或switch语句,就会用到带标签的switch语句
eg:对一个二维数组求和的实例
let arr = [1, 2, 3, [5, 67, 7], [8, , 9]];
let sum = 0, sucess = false;
compute_sum: if (arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
let row = arr[i];
if (!row) {
break compute_sum;
} else if (typeof row == 'number') {
sum += arr[i]
} else if (row instanceof Array) {
for (var j = 0; j < row.length; j++) {
if (!row[j]) break compute_sum;
sum += row[j]
}
}
}
sucess = true;
console.log(sum)
}
console.log(sucess)//false 如果sucess 为false ,说明我们给出的队列有问题
6.3 continue语句
continue语句和break语句非常类似,但是它不是退出循环,而是转而进行下一次的循环。continue语法:
◆continue;
◆continue labelname;
不管continue语句带不带标签,它只能在循环体内使用。在其他的地方使用会报错。
当执行到continue语句的时候,当前的循环逻辑就终止了,随即执行下一次循环,在不同类型的循环中,continue的行为也有区别
◆ 在while循环中,在循环开始处指定的expression,如果检测结果为true,循环体会从头开始执行
◆ do/while循环中,程序的执行直接跳到循环结尾处,这时会重新判断循环条件,之后才会进行下一次循环
◆在for循环中,首先计算自增表达式,然后检测test表达式,用来判断执行循环体
◆在for/in循环中,循环开始遍历下一个属性名,这个属性名赋值给指定遍量
eg:
let data = ['sss', 'sss', , 'women'];
let str = '';
for (var i = 0; i < data.length; i++) {
if (!data[i]) {
continue
} else {
str += i + data[i]
}
}
console.log(str)//0sss1sss3women
let data = ['sss', 'sss', , 'women'];
let str = '';
var i = 0;
do {
if (!data[i]) {
continue
} else {
str += i + data[i]
}
} while (i++ < data.length);
console.log(str)
6.4 return 语句
回想一下,函数调用是一种表达式,而所有的表达式是有值。函数中的return语句是指函数调用后的返回值。return语句
return expression;
◆return语句只能在函数体内容出现,如果不是会报语法错误
◆当执行到return语句值,函数会终止操作,并返回expression值
◆如果函数内没有return语句,则返回undefined
◆ return语句会放在函数最后面
◆return 后面没有任何值,则返回undefined
eg:
function square(x){
return x*x;
}
square(2)
6.5throw语句
所谓异常是当发生了某种异常情况或错误时会产生的一个信号。抛出异常,就是用信号通知发生了错误或异常情况,捕获异常是处理这个信号,即采用必要的手段从异常恢复正常。使用 try/catch/finally语句可以捕获异常
throw语句的语法如下:
throw expression
expression的值可以是任意类型的。可以抛出一个代表错误码的数字,或者包括可读的错误信息的字符串。当js解释器抛出异常的时候通常采用Error类型和其子类型,单个也可以使用它们。一个Error对象有一个name属性表示错误异常,一个message属性来传递给构造函数的字符串,eg:
function square(x){
if(x<0){
throw new Error('x不能是负数')
}
return x;
}
square(2)
当抛出异常的时候,js解释器会停止当前正在运行的逻辑,并转至就近异常的处理程序。异常处理程序使用try/catch/finally语句的catch从句编写。
如果抛出的异常的代码块没有一条相关联的catch语句,解释器会检查更高层闭合代码块。以此类推,知道找到一个异常处理程序位置。如果没有找到任何异常处理程序,js将异常当做错误程序处理
6.6try/catch/finally
try/catch/finally语句是js的异常处理机制。其中try从句定义了需要处理的异常代码块。catch从句跟随try从句之后,当try块内某处发生了异常,调用catch内的代码逻辑。catch从句跟随finally块,后者中放置清理代码,不管try块中是否产生异常,finally块内的逻辑总会执行。尽管catch和finally都是可选的,但是try从句需要至少二者之一的完整语句。try,catch和finally语句块都需要使用花括号括起来,即使从句之一一条语句也不能省略花括号
下面的代码说明了try/catch/finally的语法和使用目的
注:catch后面跟一个圆括号,圆括号内有个标识符,这个标识符和函数参数很像。和普通变量不同,这条catch子句的标识符具有块级作用域,它只在catch语句块定义
try{
//通常来讲,这里的代码从头执行到尾而不会产生任何问题
//但有时会抛出一个异常,由throw语句直接抛出异常
//要么是通过调用一个方法直接抛出异常
}catch(e){
//当try语句抛出了异常,这里的逻辑总是会执行
//这里可以通过局部变量e来获取对Error对象或者抛出的其他值的引用
//这里的代码可以基于某种原因处理这个异常,也可以直接忽略这个异常
//还可以通过throw语句直接抛出异常
}finally{
//不管try语句块是否抛出了异常,这里的逻辑总会执行,终止try语句块的方式有
//1)正常终止,执行完语句块的最后一条语句
//2)通过break,continue或return语句终止
//3)抛出一个异常,异常被catch从句捕获
//4)抛出一个异常,异常未被捕获,继续向上传播
}
eg:
try{
alert(i);
}catch(e){
alert(e.message)//弹出一个提示框,提示ii is not defined
}
var foo = function (x) {
try {
if (isNaN(x)) {
throw new Error('i 不是一个数字')
}
} finally {
return 1;
}
}
console.log(foo(1));//1
7.其他语句类型
js语句----width,debugger和use strict
7.1with语句
◆with用于临时扩展作用域链,它具有以下语法
with(object){
statement
}
◆这条语句将object添加到作用域头部,然后执行statement,最后把作用域链恢复到原始状态
◆在严格模式中是禁止使用with语句,并且在非严格模式里也是不推荐使用with语句的,尽可能避免with语句。那些使用with语句的js代码非常难优化,并且同没有使用with语句的代码相比,它运行的很慢
◆它的作用是可以用最简单的方法给object的属性赋值,以下的例子为例
◆这种方法减少了大量的输入,不再为每个属性名添加o,
◆但是需要注意的是,只有查找标识符的时候才会用到作用域,创建新的变量,如对象o需要有属性x,它才能执行 with(o)代码块的x=1,否则赋值不成功
eg:
let o={x:"",y:''};
with(o){
x=1;/给o的x赋值为1
y=2;//给o的y赋值为2
z=3;
}
console.log(o.x)//1
console.log(o.z)//1undefined
相当于
let o={};
o.x=1;
o.y=2;
7.2debugger
debugger语句通常什么也不做。然而在程序可用并运行的时候,js将以调试模式进行。实际上,这条语句会产生一个断点,js的执行会停止到断点的位置,这时可以调试输出变量的值,检测调用栈等
eg:
debugger
let o={x:"",y:''};
with(o){
x=1
y=2
z=3
}
console.log(o.z)//1
7.3 'use strict'
◆使用'use strict'的指令的目的是说明(脚本或函数中)后续的代码会解析成严格模式
◆如果顶层代码使用了‘use strict’ 指令,那么它们就是严格模式代码
◆如果在函数体内使用‘use strict’ 指令,那么函数体就是严格模式代码
严格模式和非严格模式区别如下
◆ 在严格模式中禁止使用with语句
◆严格模式中,所有的变量要声明,如果给一个未声明的变量,函数,函数参数,catch从语句参数或全局对象属性值,会抛出一个引用异常的错误
◆ 在严格模式中,调用函数中的this是一个undefined(非严格模式是window)
◆ 在严格模式中,当通过call()或apply()来调用函数时,其中的this值就是通过call和aplly传入的第一个参数(非严格模式中,null和undefined值被全局对象和转换为对象的非对象值所代替)
◆ 在严格模式下,给只读属性赋值和给不给可扩展的对象创建新成员都将抛出一个类型错误异常(在非严格模式中,这些操作只是简单的操作,不会报错)
◆ 在严格模式中,传入eva()的代码不能在调用程序所在的上下文声明变量或定义函数,而在非严格模式下可以
◆ 在严格模式中,函数的arguments对象拥有传入函数值的静态副本。也就是就是说无论函数的参数有没有传递,都不会和arguments相互影响
◆ 在严格模式中,当delete运算符后跟随非法的标识符,将会抛出一个语法错误(在非严格模式中,会返回一个false)
◆ 在严格模式中,delete删除一个不可匹配的属性将抛出一个类型错误异常(在非严格模式中,delete表达式错误,会返回一个false)
◆ 在严格模式中,在一个对象直接量定义两个或多个同名的属性会报错
◆在严格模式中,函数声明中存在两个或者多个同名的参数将产生一个语法错误
◆在严格模式中,标识符eval和arguments当做关键字,它们的值是不能更改的。也不能给这些标识符赋值,也不能把它们声明为变量,用作函数名,用做函数参数或catch块的标识符
◆在严格模式中限制了对调用栈的检测能力,在严格模式的函数中,arguments,caller和arguments.callee都会抛出一个类型错误异常
"use strict"
//i=1//Uncaught ReferenceError: i is not defined
function aa() {
console.log(this)//undefined
}
aa()
let obj = { x: 1, y: 2 }
delete obj.z;
let getObj = function (key) {
console.log(this[key]);//1
}
//getObj.call(null,'x')//Uncaught TypeError: Cannot read property 'x' of null
getObj.call(obj, 'x')//1
let g = eval;
let y = '全局';
function eleObj() {
let y = '局部';
g('y+="sss"');
return y;
}
console.log(y, eleObj())//全局 局部 没有改变任何东西
function test(a) {
arguments[0].x = 100;
console.log(a.x);//100
}
test({ x: 1 });