第三章 基本概念(下) --《Javascript高级程序设计》

六、语句

6.1 if语句

大多数编程语言中最为常用的一个语句就是if 语句。以下是if 语句的语法:
if (condition) statement1 else statement2
其中的condition(条件)可以是任意表达式;而且对这个表达式求值的结果不一定是布尔值。ECMAScript 会自动调用Boolean()转换函数将这个表达式的结果转换为一个布尔值。如果对condition求值的结果是true,则执行statement1(语句1),如果对condition 求值的结果是false,则执行statement2(语句2)。而且这两个语句既可以是一行代码,也可以是一个代码块(以一对花括号括起来的多行代码)。
请看下面的例子。
if (i > 25)
  alert("Greater than 25."); // 单行语句
else {
  alert("Less than or equal to 25."); // 代码块中的语句
}

另外,也可以像下面这样把整个if 语句写在一行代码中:
if (condition1) statement1 else if (condition2) statement2 else statement3

if (i > 25) {
  alert("Greater than 25.");
} else if (i < 0) {
  alert("Less than 0.");
} else {
  alert("Between 0 and 25, inclusive.");
}

6.2 do-while语句

do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。
换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次。以下是do-while 语句的语法:
do {
  statement
} while (expression);
下面是一个示例:
var i = 0;
do {
  i += 2;
} while (i < 10);
alert(i);

在这个例子中,只要变量i 的值小于10,循环就会一直继续下去。而且变量i 的值最初为0,每次循环都会递增2。

6.3 while语句

while 语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。
因此,循环体内的代码有可能永远不会被执行。以下是while 语句的语法:
while(expression) statement
下面是一个示例:
var i = 0;
while (i < 10) {

  i += 2;

}

在这个例子中,变量i 开始时的值为0,每次循环都会递增2。而只要i 的值小于10,循环就会继续下去。

6.4 for语句

for 语句也是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代码的能力。以下是for 语句的语法:
for (initialization; expression; post-loop-expression) statement
下面是一个示例:
var count = 10;
for (var i = 0; i < count; i++){
  alert(i);
}

以上代码定义了变量i 的初始值为0。只有当条件表达式(i<count)返回true 的情况下才会进入for 循环,因此也有可能不会执行循环体中的代码。如果执行了循环体中的代码,则一定会对循环后的表达式(i++)求值,即递增i 的值。这个for 循环语句与下面的while 语句的功能相同:
var count = 10;
var i = 0;
while (i < count){
alert(i);
i++;
}
使用while 循环做不到的,使用for 循环同样也做不到。也就是说,for 循环只是把与循环有关的代码集中在了一个位置。
有必要指出的是,在for 循环的变量初始化表达式中,也可以不使用var 关键字。该变量的初始化可以在外部执行,例如:
var count = 10;
var i;
for (i = 0; i < count; i++){
alert(i);
}

由于ECMAScript 中不存在块级作用域,因此在循环内部定义的变量也可以在外部访问到。例如:

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

即使i 是在循环内部定义的一个变量,但在循环外部仍然可以访问到它。

此外,for 语句中的初始化表达式、控制表达式和循环后表达式都是可选的。将这三个表达式全部省略,就会创建一个无限循环,例如:
for (;;) { // 无限循环
  doSomething();
}
而只给出控制表达式实际上就把for 循环转换成了while 循环,例如:
var count = 10;
var i = 0;
for (; i < count; ){
  alert(i);
  i++;
}

6.5 for-in语句

for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性。以下是for-in 语句的语法:
for (property in expression) statement
下面是一个示例:
for (var propName in window) {
  document.write(propName);
}

在这个例子中,我们使用for-in 循环来显示了BOM 中window 对象的所有属性。每次执行循环时,都会将window 对象中存在的一个属性名赋值给变量propName。这个过程会一直持续到对象中的所有属性都被枚举一遍为止。与for 语句类似,这里控制语句中的var 操作符也不是必需的。但是,为了保证使用局部变量,我们推荐上面例子中的这种做法。
ECMAScript 对象的属性没有顺序。因此,通过for-in 循环输出的属性名的顺序是不可预测的。具体来讲,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。

但是,如果表示要迭代的对象的变量值为null 或undefined,for-in 语句会抛出错误。ECMAScript 5 更正了这一行为;对这种情况不再抛出错误,而只是不执行循环体。为了保证最大限度的兼容性,建议在使用for-in 循环之前,先检测确认该对象的值不是null 或undefined。

6.6 label语句

使用label 语句可以在代码中添加标签,以便将来使用。以下是label 语句的语法:
label: statement
下面是一个示例:
start: for (var i=0; i < count; i++) {
  alert(i);
}
这个例子中定义的start 标签可以在将来由break 或continue 语句引用。加标签的语句一般都要与for 语句等循环语句配合使用。

6.7 break和continue语句

break 和continue 语句用于在循环中精确地控制代码的执行。其中,break 语句会立即退出循环,强制继续执行循环后面的语句。而continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行。请看下面的例子:

var num = 0;
for (var i=1; i < 10; i++) {
  if (i % 5 == 0) {
    break;
  }
  num++;
}
alert(num); //4

在执行break 语句之后,要执行的下一行代码是alert()函数,结果显示4。也就是说,在变量i 等于5 时,循环总共执行了4 次;而break 语句的执行,导致了循环在num 再次递增之前就退出了。如果在这里把break 替换为continue 的话,则可以看到另一种结果:

var num = 0;
for (var i=1; i < 10; i++) {
  if (i % 5 == 0) {
    continue;
  }

  num++;
}
alert(num); //8

例子的结果显示8,也就是循环总共执行了8 次。当变量i 等于5 时,循环会在num 再次递增之前退出,但接下来执行的是下一次循环,即i 的值等于6 的循环。于是,循环又继续执行,直到i 等于10 时自然结束。而num 的最终值之所以是8,是因为continue 语句导致它少递增了一次。

break 和continue 语句都可以与label 语句联合使用,从而返回代码中特定的位置。这种联合使用的情况多发生在循环嵌套的情况下,如下面的例子所示:
var num = 0;
outermost:
for (var i=0; i < 10; i++) {
  for (var j=0; j < 10; j++) {
    if (i == 5 && j == 5) {
      break outermost;
    }
    num++;
  }
}
alert(num); //55

outermost 标签表示外部的for 语句。如果每个循环正常执行10 次,则num++语句就会正常执行100 次。换句话说,如果两个循环都自然结束,num 的值应该是100。但内部循环中的break 语句带了一个参数:要返回到的标签。添加这个标签的结果将导致break 语句不仅会退出内部的for 语句(即使用变量j 的循环),而且也会退出外部的for 语句(即使用变量i 的循环)。为此,当变量i 和j 都等于5 时,num 的值正好是55。

continue语句也可以像这样与label语句联用,例如:

var num = 0;
outermost:
for (var i=0; i < 10; i++) {
  for (var j=0; j < 10; j++) {
    if (i == 5 && j == 5) {
      continue outermost;
    }
    num++;
  }
}
alert(num); //95

在这种情况下,continue 语句会强制继续执行循环——退出内部循环,执行外部循环。当j 是5时,continue 语句执行,而这也就意味着内部循环少执行了5 次,因此num 的结果是95。

6.8 with语句

with 语句的作用是将代码的作用域设置到一个特定的对象中。with 语句的语法如下:
with (expression) statement;
定义with 语句的目的主要是为了简化多次编写同一个对象的工作,如下面的例子所示:
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
上面几行代码都包含location 对象。如果使用with 语句,可以把上面的代码改写成如下所示:
with(location){
  var qs = search.substring(1);
  var hostName = hostname;
  var url = href;
}

在这个重写后的例子中,使用with 语句关联了location 对象。这意味着在with 语句的代码块内部,每个变量首先被认为是一个局部变量,而如果在局部环境中找不到该变量的定义,就会查询location 对象中是否有同名的属性。如果发现了同名属性,则以location 对象属性的值作为变量的值。
严格模式下不允许使用with 语句,否则将视为语法错误。

由于大量使用with 语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用with 语句。

6.9 switch语句

switch 语句与if 语句的关系最为密切,而且也是在其他语言中普遍使用的一种流控制语句。
ECMAScript 中switch 语句的语法与其他基于C 的语言非常接近,如下所示:
switch (expression) {
  case value: statement
    break;
  case value: statement
    break;
  case value: statement
    break;
  case value: statement
    break;
  default: statement
}

switch 语句中的每一种情形(case)的含义是:“如果表达式等于这个值(value),则执行后面的语句(statement)”。而break 关键字会导致代码执行流跳出switch 语句。如果省略break 关键字,就会导致执行完当前case 后,继续执行下一个case。最后的default 关键字则用于在表达式不匹配前面任何一种情形的时候,执行机动代码(因此,也相当于一个else 语句)。
从根本上讲,switch 语句就是为了让开发人员免于编写像下面这样的代码:

if (i == 25){
  alert("25");
} else if (i == 35) {
  alert("35");
} else if (i == 45) {
  alert("45");
} else {
  alert("Other");
}
而与此等价的switch 语句如下所示:
switch (i) {
  case 25:
    alert("25");
    break;
  case 35:
    alert("35");
    break;
  case 45:
    alert("45");
    break;
  default:
    alert("Other");
}

通过为每个case 后面都添加一个break 语句,就可以避免同时执行多个case 代码的情况。假如确实需要混合几种情形,不要忘了在代码中添加注释,说明你是有意省略了break 关键字,如下所示:
switch (i) {
  case 25:  
  case 35:      /* 合并两种情形 */
    alert("25 or 35");
    break;
  case 45:
    alert("45");
    break;
  default:
    alert("Other");
}

这个语句也有自己的特色,首先,可以在switch 语句中使用任何数据类型(在很多其他语言中只能使用数值),无论是字符串,还是对象都没有问题。其次,每个case 的值不一定是常量,可以是变量,甚至是表达式。请看下面这个例子:
switch ("hello world") {
  case "hello" + " world":
    alert("Greeting was found.");
    break;
  case "goodbye":
    alert("Closing was found.");
    break;
  default:
    alert("Unexpected message was found.");
}

第一种情形实际上是一个对字符串拼接操作求值的表达式。由于这个字符串拼接表达式的结果与switch 的参数相等,因此结果就会显示"Greeting was found."。而且,使用表达式作为case 值还可以实现下列操作:

var num = 25;
switch (true) {
  case num < 0:
    alert("Less than 0.");
    break;
  case num >= 0 && num <= 10:
    alert("Between 0 and 10.");
    break;
  case num > 10 && num <= 20:
    alert("Between 10 and 20.");
    break;
  default:
    alert("More than 20.");
}

这个例子首先在switch 语句外面声明了变量num。而之所以给switch 语句传递表达式true,是因为每个case 值都可以返回一个布尔值。

switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换(例如,字符串"10"不等于数值10)。

七、函数

通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。ECMAScript 中的函数使用function 关键字来声明,后跟一组参数以及函数体。函数的基本语法如下所示:

function functionName(arg0, arg1,...,argN) {
  statements
}
以下是一个函数示例:
function sayHi(name, message) {
  alert("Hello " + name + "," + message);
}

这个函数可以通过其函数名来调用,后面还要加上一对圆括号和参数(圆括号中的参数如果有多个,可以用逗号隔开)。调用sayHi()函数的代码如下所示:
sayHi("Nicholas", "how are you today?");

这个函数的输出结果是"Hello Nicholas,how are you today?"。函数中定义中的命名参数name和message 被用作了字符串拼接的两个操作数,而结果最终通过警告框显示了出来。
ECMAScript 中的函数在定义时不必指定是否返回值。实际上,任何函数在任何时候都可以通过return 语句后跟要返回的值来实现返回值。请看下面的例子:
function sum(num1, num2) {
  return num1 + num2;
}

这个sum()函数的作用是把两个值加起来返回一个结果。我们注意到,除了return 语句之外,没有任何声明表示该函数会返回一个值。调用这个函数的示例代码如下:
var result = sum(5, 10);
这个函数会在执行完return 语句之后停止并立即退出。因此,位于return 语句之后的任何代码都永远不会执行。例如:
function sum(num1, num2) {
return num1 + num2;
alert("Hello world"); // 永远不会执行
}

由于调用alert()函数的语句位于return 语句之后,因此永远不会显示警告框。
当然,一个函数中也可以包含多个return 语句,如下面这个例子中所示:
function diff(num1, num2) {
  if (num1 < num2) {
    return num2 - num1;
  } else {
    return num1 - num2;
  }
}

这个例子中定义的diff()函数用于计算两个数值的差。如果第一个数比第二个小,则用第二个数
减第一个数;否则,用第一个数减第二个数。代码中的两个分支都具有自己的return 语句,分别用于
执行正确的计算。
另外,return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回undefined
值。这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下。比如在下面这个例子中,就
不会显示警告框:
function sayHi(name, message) {
  return;
  alert("Hello " + name + "," + message); //永远不会调用
}

严格模式对函数有一些限制:
? 不能把函数命名为eval 或arguments;
? 不能把参数命名为eval 或arguments;
? 不能出现两个命名参数同名的情况。
如果发生以上情况,就会导致语法错误,代码无法执行。

7.1 理解参数

ECMAScript 函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数,在调用这个函数时也未必一定要传递两个参数。可以传递一个、三个甚至不传递参数,而解析器永远不会有什么怨言。之所以会这样,原因是ECMAScript 中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任何元素,无所谓;如果包含多个元素,也没有问题。实际上,在函数体内可以通过arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数。

其实,arguments 对象只是与数组类似(它并不是Array 的实例),因为可以使用方括号语法访问它的每一个元素(即第一个元素是rguments[0],第二个元素是argumetns[1],以此类推),使用length 属性来确定传递进来多少个参数。在前面的例子中,sayHi()函数的第一个参数的名字叫name,而该参数的值也可以通过访问arguments[0]来获取。因此,那个函数也可以像下面这样重写,即不显式地使用命名参数:
function sayHi() {
  alert("Hello " + arguments[0] + "," + arguments[1]);
}

这个重写后的函数中不包含命名的参数。虽然没有使用name 和message 标识符,但函数的功能依旧。这个事实说明了ECMAScript 函数的一个重要特点:命名的参数只提供便利,但不是必需的。另外,在命名参数方面,其他语言可能需要事先创建一个函数签名,而将来的调用必须与该签名一致。但在ECMAScript 中,没有这些条条框框,解析器不会验证命名参数。

通过访问arguments 对象的length 属性可以获知有多少个参数传递给了函数。下面这个函数会
在每次被调用时,输出传入其中的参数个数:
function howManyArgs() {
  alert(arguments.length);
}
howManyArgs("string", 45); //2
howManyArgs(); //0
howManyArgs(12); //1

开发人员可以利用这一点让函数能够接收任意个参数并分别实现适当的功能。请看下面的例子:
function doAdd() {
  if(arguments.length == 1) {
    alert(arguments[0] + 10);
  } else if (arguments.length == 2) {
    alert(arguments[0] + arguments[1]);
  }
}
doAdd(10); //20
doAdd(30, 20); //50

另一个与参数相关的重要方面,就是arguments 对象可以与命名参数一起使用,如下面的例子所示:
function doAdd(num1, num2) {
  if(arguments.length == 1) {
    alert(num1 + 10);
  } else if (arguments.length == 2) {
    alert(arguments[0] + num2);
  }
}

关于arguments 的行为,还有一点比较有意思。那就是它的值永远与对应命名参数的值保持同步。例如:

function doAdd(num1, num2) {
  arguments[1] = 10;
  alert(arguments[0] + num2);
}

doAdd(5,2);//15

doAdd(5);//NaN

每次执行这个doAdd()函数都会重写第二个参数,将第二个参数的值修改为10。因为arguments对象中的值会自动反映到对应的命名参数,所以修改arguments[1],也就修改了num2,结果它们的值都会变成10。不过,这并不是说读取这两个值会访问相同的内存空间;它们的内存空间是独立的,但它们的值会同步。另外还要记住,如果只传入了一个参数,那么为arguments[1]设置的值不会反应到命名参数中。这是因为arguments 对象的长度是由传入的参数个数决定的,不是由定义函数时的命名参数的个数决定的。
关于参数还要记住最后一点:没有传递值的命名参数将自动被赋予undefined 值。这就跟定义了变量但又没有初始化一样。例如,如果只给doAdd()函数传递了一个参数,则num2 中就会保存undefined 值。
严格模式对如何使用arguments 对象做出了一些限制。首先,像前面例子中那样的赋值会变得无效。也就是说,即使把arguments[1]设置为10,num2 的值仍然还是undefined。其次,重写arguments 的值会导致语法错误(代码将不会执行)。

ECMAScript 中的所有参数传递的都是值,不可能通过引用传递参数。

7.2 没有重载

ECMAScript 函数不能像传统意义上那样实现重载。而在其他语言(如Java)中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。如前所述,ECMAScirpt函数没有签名,因为其参数是由包含零或多个值的数组来表示的。而没有函数签名,真正的重载是不可能做到的。
如果在ECMAScript 中定义了两个名字相同的函数,则该名字只属于后定义的函数,由于后定义的函数覆盖了先定义的函数。请看下面的例子:
function addSomeNumber(num){
  return num + 100;
}
function addSomeNumber(num) {
  return num + 200;
}
var result = addSomeNumber(100); //300

如前所述,通过检查传入函数中参数的类型和数量并作出不同的反应,可以模仿方法的重载。

时间: 2024-10-21 05:54:31

第三章 基本概念(下) --《Javascript高级程序设计》的相关文章

第三章 基本概念 --《Javascript高级程序设计》

一.语法 1.区分大小写 ECMAScript 中的一切(变量.函数名和操作符)都区分大小写. 2.标识符 所谓标识符,就是指变量.函数.属性的名字,或者函数的参数. 标识符可以是按照下列格式规则组合起来的一或多个字符: 第一个字符必须是一个字母.下划线(_)或一个美元符号($): 其他字符可以是字母.下划线.美元符号或数字. 按照惯例,ECMAScript 标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的首字母大写,例如: firstSecond    myCar     do

《Javascript高级程序设计》阅读记录(三):第五章 上

这个系列以往文字地址: <Javascript高级程序设计>阅读记录(一):第二.三章 <Javascript高级程序设计>阅读记录(二):第四章 这个系列,我会把阅读<Javascript高级程序设计>之后,感觉讲的比较深入,而且实际使用价值较大的内容记录下来,并且注释上我的一些想法.做这个一方面是提升了我的阅读效果以及方便我以后阅读 另一个目的是,Javascript高级程序设计这本书内容很多也很厚,希望其他没有时间的人可以通过看这系列摘录,就可以大体学到书里面的核

Javascript高级程序设计——第三章:基本概念

javascript高级程序设计——第三章:基本概念 一.语法 EMCA-262通过叫做ECMAScript的“伪语言”为我们描述了javascript实现的基本概念 javascript借鉴了C的语法,区分大小写,标示符以字母.下划线.或美元符号($)开头,注释可以用 // 或者/* */ 严格模式: ECMAScript 5引入了严格模式,在严格模式下不确定的行为将得到处理,通过在顶部添加 “use strict”来启用严格模式: function fuc(){ "use strict&qu

《javascript高级程序设计》第二、三章知识点整理

第二章知识点总结 1.<script>在html中的使用 主要功能:在页面中嵌入javascript代码或包含外部javascript文件. 常用属性: type:用于定义脚步代码的语言类型,默认为text/javascript. src:包含外部域的javascript文件. defer:表示脚本会在整个页面加载完毕之后运行,只对外部文件有效,最好只包含一个延迟脚本. async:脚本在不妨碍其他操作的情况下立即下载(不保证下载文件的顺序). 插入位置:在页面<body>标签页面

JavaScript高级程序设计(第三版)学习笔记20、21、23章

第20章,JSON JSON(JavaScript Object Notation,JavaScript对象表示法),是JavaScript的一个严格的子集. JSON可表示一下三种类型值: 简单值:字符串,数值,布尔值,null,不支持js特殊值:undefined 对象:一组无序的键值对 数组:一组有序的值的列表 不支持变量,函数或对象实例 注:JSON的字符串必须使用双引号,这是与JavaScript字符串最大的区别 对象 { "name":"Nicholas"

JavaScript高级程序设计(第3版)第三章读书笔记

第三章  基本概念 ECMAScript中的一切(变量.函数名和操作符)都区分大小写. 标识符是指变量.函数.属性的名字,或者函数的参数. 标识符的组成规则是:第一个字符必须是一个字母.下划线(_)或一个美元符号($):其他字符可以是字母.下划线.美元符号或数字. ECMAScript标识符采用驼峰大小写格式. ECMAScript注释包括单行注释(//)和块级注释(/*  *  */). ECMAScript 5引入了严格模式的概念,严格模式是为JavaScript定义了一种不同的解析与执行模

《JAVASCRIPT高级程序设计》第三章

<JAVASCRIPT高级程序设计>第三章主要讲述了这门语言的基础概念,内容多而浅,通过思维导图可以帮助我们很好的理清脉络. js函数使用function关键字来声明,以下是一个简单的例子: 1 function sayHi(name, message){ 2 alert("hello," +name + message); 3 } 函数中可以使用return语句指定返回值,如果只有“return;”,则函数在停止执行后,将返回undefined; 定义的函数有2个参数,那

《Javascript高级程序设计》阅读记录(四):第五章 下

这个系列,我会把阅读<Javascript高级程序设计>之后,感觉讲的比较深入,而且实际使用价值较大的内容记录下来,并且注释上我的一些想法.做这个一方面是提升了我的阅读效果以及方便我以后阅读 另一个目的是,Javascript高级程序设计这本书内容很多也很厚,希望其他没有时间的人可以通过看这系列摘录,就可以大体学到书里面的核心内容. 绿色背景的内容是我认为比较值得注意的原著内容. 黄色背景的内容是我认为非常重要的原著内容. 我的理解会用蓝色的字体标示出来. 这章的内容较多,而且比较重要,分两篇

javascript高级程序设计 第十三章--事件

javascript高级程序设计 第十三章--事件js与HTML的交互就是通过事件实现的,事件就是文档或浏览器窗口中发生的一些特定的交互瞬间. 事件流:事件流描述的是从页面中接收事件的顺序,IE的是事件冒泡流,Netscape的是事件捕获流,这个两个是完全相反的事件流概念. 事件冒泡:由最具体的元素接收,然后逐级向上传播到更高级的节点,即事件沿DOM树向上传播,直到document对象. 事件捕获:不大具体的节点应该更早接收到事件,相当于沿DOM节点树向下级传播直到事件的实际目标,在浏览器中,是

《javascript高级程序设计》笔记三

第三章 基本概念 任何语言的核心必然会描述这门语言最基本的工作原理.这部分内容对我们来说,读起来很乏味,甚至会产生困意,但这部分内容却是重要的!我有幸拜读<JavaScript高级程序设计>,更具我自己工作中体会,下面列出一些注意点和有用的东西(⊙o⊙)- ①命名规则.推荐采用驼峰命名.比如:userName. ②块级注释. 1 /* 2 * 推荐块级注释,这里对代码进行注释说明! 3 */ ③JavaScript中采用严格模式.在严格模式下,ECMAScript3中的一些不确定的行为将得到处