目录
3.基本概念
3.1语法
3.1.1区分大小写
ECMAScript中的一切(变量、函数名和操作符)都区分大小写。
3.1.2标识符
所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符可以是按照下列格式规则 组合起来的一或多个字符:
第一个字符必须是一个字母、下划线(_)或一个美元符号($);
其他字符可以是字母、下划线、美元符号或数字。 按照惯例,ECMAScript 标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的 首字母大写。
虽然没有谁强制要求必须采用这种格式,但为了与 ECMAScript内置的函数和对象命名格式保持一 致,可以将其当作一种佳实践。
3.1.3注释
单行注释:// 被注释代码块
块级注释:/* 被注释代码块 */
3.1.4严格模式
ECMAScript 5引入了严格模式(strict mode)的概念。严格模式是为 JavaScript定义了一种不同的 解析与执行模型。在严格模式下,ECMAScript 3中的一些不确定的行为将得到处理,而且对某些不安全 的操作也会抛出错误。要在整个脚本中启用严格模式,可以在顶部添加如下代码: "use strict";
这行代码看起来像是字符串,而且也没有赋值给任何变量,但其实它是一个编译指示(pragma), 用于告诉支持的 JavaScript引擎切换到严格模式。在函数内部的上方包含这条编译指示,也可以指定函数在严格模式下执行:
function doSomething(){ "use strict"; //函数体 }
严格模式下,JavaScript 的执行结果会有很大不同,支 持严格模式的浏览器包括 IE10+、Firefox 4+、Safari 5.1+、Opera 12+和 Chrome
3.1.5语句
ECMAScript中的语句以一个分号结尾;如果省略分号,则由解析器确定语句的结尾。
虽然语句结尾的分号不是必需的,但我们建议任何时候都不要省略它。因为加上这个分号可以避免 很多错误(例如不完整的输入),开发人员也可以放心地通过删除多余的空格来压缩 ECMAScript代码(代 码行结尾处没有分号会导致压缩错误)。另外,加上分号也会在某些情况下增进代码的性能,因为这样 解析器就不必再花时间推测应该在哪里插入分号了。
3.2关键字和保留字
关键字:关键字可用于表示控制语句的开始或结束,或 者用于执行特定操作等。按照规则,关键字不能用作标识符
保留字:保留字在这门语言中还没有任何特定 的用途,但它们有可能在将来被用作关键字,保留字不能用作标识符
3.3变量
ECMAScript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说, 每个变量仅仅是一个用于保存值的占位符而已。定义变量时要使用 var 操作符(注意 var 是一个关键 字),后跟变量名(即一个标识符)。
未经过初始化的变量的类型是Undefined,
使用var来声明的变量是该作用域内的局部变量
例如在函数中 用var定义一个变量,当函数被调用时,就会创建该变量并为其赋值。 函数调用完之后,这个变量又会立即被销毁,可以像下面这 样省略 var 操作符,从而创建一个全局变量(不推荐):
function test(){ message = "hi"; // 全局变量 } test(); alert(message); // "hi"
这个例子省略了 var 操作符,因而 message 就成了全局变量。这样,只要调用过一次 test()函 数,这个变量就有了定义,就可以在函数外部的任何地方被访问到。
3.4数据类型
6种基本数据类型:Number.String.Boolean.Null.Undefined.Symbal
1种复杂数据类型:Object(Object 本质上是由一组无序的名值对组成的)
3.4.1 typeof操作符
typeof()是操作符,不是函数;
type可用于变量也可用于字面量。typeof的结果肯定是下面所列的其中一种:
"undefined"——如果这个值未定义;
"boolean"——如果这个值是布尔值;
"string"——如果这个值是字符串;
"number"——如果这个值是数值;
"object"——如果这个值是对象或 null;
"function"——如果这个值是函数。
3.4.2 Undefined类型
Undefined 类型只有一个值,即特殊的 undefined。在使用 var 声明变量但未对其加以初始化时, 这个变量的值就是 undefined。
// 下面这个变量并没有声明(没分号) //
var age
alert(message);// "undefined"
alert(age); // 产生错误
运行以上代码,第一个警告框会显示变量 message 的值,即"undefined"。而第二个警告框—— 由于传递给 alert()函数的是尚未声明的变量 age——则会导致一个错误。对于尚未声明过的变量,只 能执行一项操作,即使用 typeof 操作符检测其数据类型
然而,令人困惑的是:对未初始化的变量执行 typeof 操作符会返回 undefined 值,而对未声明 的变量执行 typeof 操作符同样也会返回 undefined 值。来看下面的例子:
var message; // 这个变量声明之后默认取得了 undefined 值
// 下面这个变量并没有声明 //
var age
alert(typeof message); // "undefined"
alert(typeof age); // "undefined"
UndefinedExample04.htm
结果表明,对未初始化和未声明的变量执行 typeof 操作符都返回了 undefined 值;这个结果有 其逻辑上的合理性。因为虽然这两种变量从技术角度看有本质区别,但实际上无论对哪种变量也不可能 执行真正的操作。
即便未初始化的变量会自动被赋予 undefined 值,但显式地初始化变量依然是 明智的选择。如果能够做到这一点,那么当 typeof 操作符返回"undefined"值时, 我们就知道被检测的变量还没有被声明,而不是尚未初始化
3.4.3Null类型
Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null。从逻辑角度来看,null 值表 示一个空对象指针,而这也正是使用 typeof 操作符检测 null 值时会返回"object"的原因,如下面 的例子所示:
var car = null;
alert(typeof car); // "object"
这里,位于 null 和 undefined 之间的相等操作符(==)总是返回 true, 尽管 null 和 undefined 有这样的关系,但它们的用途完全不同。如前所述,无论在什么情况下 都没有必要把一个变量的值显式地设置为 undefined,可是同样的规则对 null 却不适用。换句话说, 只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存 null 值。这样做不仅可以 体现 null 作为空对象指针的惯例,而且也有助于进一步区分 null 和 undefined。
3.4.4Boolean类型
该类型只有两个字面值:true 和 false(严格区分大小写)。
要将一个值转换为其对应的 Boolean 值,可以调用转型函数 Boolean()
可以对任何数据类型的值调用 Boolean()函数,而且总会返回一个 Boolean 值。至于返回的 这个值是 true 还是 false,取决于要转换值的数据类型及其实际值。下表给出了各种数据类型及其对 应的转换规则。
对于String类型的数据调用Boolean()函数:任何非空字符串(true)//空字符串(false)
对于Number类型的数据调用Boolean()函数:非零数字(true)//NaN和0(false)
对于Object类型的数据调用Boolean()函数:任何对象(true)//null(false)
对于Object类型的数据调用Boolean()函数:n/a(true)//undefined(false)
这些转换规则对理解流控制语句(如 if 语句)自动执行相应的 Boolean 转换非常重要,请看下面 的代码:
ar message = "Hello world!"; if (message){ alert("Value is true"); }
运行这个示例,就会显示一个警告框,因为字符串 message 被自动转换成了对应的 Boolean 值 (true)。由于存在这种自动执行的 Boolean 转换,因此确切地知道在流控制语句中使用的是什么变量 至关重要。错误地使用一个对象而不是一个 Boolean 值,就有可能彻底改变应用程序的流程
3.4.5Number类型
除了最常用的10进制,还有8进制(以0开头),16进制(以0X开头)。在进行算术计算时,所有以八进制和十六进制表示的数值终都将被转换成十进制数值。 js可以保存+0和-0,二者相等。
浮点数:是该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字;由于保存浮点数值需要的内存空间是保存整数值的两倍,因此ECMAScript会不失时机地将浮点数值 转换为整数值;浮点数值的高精度是 17位小数,但在进行算术计算时其精确度远远不如整数,例如,0.1加 0.2 的结果不是 0.3,而是 0.30000000000000004,存在舍入误差;。因此,永远不 要测试某个特定的浮点数值。
数值范围: Number.MIN_VALUE 中——在大多数浏览器中,这个值是 5e-324;能够表示的大数值保存在 Number.MAX_VALUE 中——在大多数浏览器中,这个值是 1.7976931348623157e+308。如果某次计算的 结果得到了一个超出 JavaScript数值范围的值,那么这个数值将被自动转换成特殊的 Infinity 值。具 体来说,如果这个数值是负数,则会被转换成-Infinity(负无穷),如果这个数值是正数,则会被转 换成 Infinity(正无穷)。 如上所述,如果某次计算返回了正或负的 Infinity 值,那么该值将无法继续参与下一次的计算, 因为 Infinity 不是能够参与计算的数值。要想确定一个数值是不是有穷的(换句话说,是不是位于 小和大的数值之间),可以使用 isFinite()函数。这个函数在参数位于小与大数值之间时会返 回 true,如下面的例子所示:
var result = Number.MAX_VALUE + Number.MAX_VALUE; alert(isFinite(result)); //false
尽管在计算中很少出现某些值超出表示范围的情况,但在执行极小或极大数值的计算时,检测监控 这些值是可能的,也是必需的
NaN:即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数 未返回数值的情况(这样就不会抛出错误了)。例如,在其他编程语言中,任何数值除以 0都会导致错误, 从而停止代码执行。但在ECMAScript中,任何数值除以 0会返回 NaN①,因此不会影响其他代码的执行。 NaN 本身有两个非同寻常的特点。首先,任何涉及 NaN 的操作(例如 NaN/10)都会返回 NaN,这 个特点在多步计算中有可能导致问题。其次,NaN 与任何值都不相等,包括 NaN 本身。例如,下面的代 码会返回 false:
alert(NaN == NaN); //false
针对 NaN 的这两个特点,ECMAScript定义了 isNaN()函数。这个函数接受一个参数,该参数可以 是任何类型,而函数会帮我们确定这个参数是否“不是数值”。isNaN()在接收到一个值之后,会尝试 将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串"10"或 Boolean 值。而任何 不能被转换为数值的值都会导致这个函数返回 true。请看下面的例子:
alert(isNaN(NaN)); //true
alert(isNaN(10)); //false(10是一个数值)
alert(isNaN("10")); //false(可以被转换成数值 10)
alert(isNaN("blue")); //true(不能转换成数值)
alert(isNaN(true)); //false(可以被转换成数值 1)
尽管有点儿不可思议,但 isNaN()确实也适用于对象。在基于对象调用 isNaN() 函数时,会首先调用对象的 valueOf()方法,然后确定该方法返回的值是否可以转 换为数值。如果不能,则基于这个返回值再调用 toString()方法,再测试返回值。 而这个过程也是 ECMAScript中内置函数和操作符的一般执行流程。
数值转换:有 3个函数可以把非数值转换为数值:Number()、parseInt()和 parseFloat()。
第一个函数, 即转型函数 Number()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。这 3个 函数对于同样的输入会有返回不同的结果。
Number()函数的转换规则如下。
如果是 Boolean 值,true 和 false 将分别被转换为 1和 0。
如果是数字值,只是简单的传入和返回。
如果是 null 值,返回 0。
如果是 undefined,返回 NaN。
如果是字符串,遵循下列规则:
如果字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值,即"1" 会变成 1,"123"会变成 123,而"011"会变成 11(注意:前导的零被忽略了);
如果字符串中包含有效的浮点格式,如"1.1",则将其转换为对应的浮点数值(同样,也会忽 略前导零);
如果字符串中包含有效的十六进制格式,例如"0xf",则将其转换为相同大小的十进制整 数值;
如果字符串是空的(不包含任何字符),则将其转换为 0; 如果字符串中包含除上述格式之外的字符,则将其转换为 NaN。
如果是对象,则调用对象的 valueOf()方法,然后依照前面的规则转换返回的值。如果转换 的结果是 NaN,则调用对象的 toString()方法,然后再次依照前面的规则转换返回的字符 串值
第二个 parseInt()函数。parseInt()函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字 符串前面的空格,直至找到第一个非空格字符。如果第一个字符不是数字字符或者负号,parseInt() 就会返回 NaN;也就是说,用 parseInt()转换空字符串会返回 NaN(Number()对空字符返回 0)。如 果第一个字符是数字字符,parseInt()会继续解析第二个字符,直到解析完所有后续字符或者遇到了 一个非数字字符。例如,"1234blue"会被转换为 1234,因为"blue"会被完全忽略。类似地,"22.5" 会被转换为 22,因为小数点并不是有效的数字字符。 如果字符串中的第一个字符是数字字符,parseInt()也能够识别出各种整数格式(即前面讨论的 十进制、八进制和十六进制数)。也就是说,如果字符串以"0x"开头且后跟数字字符,就会将其当作一 个十六进制整数;如果字符串以"0"开头且后跟数字字符,则会将其当作一个八进制数来解析。 为了更好地理解 parseInt()函数的转换规则,下面给出一些例子:
在使用 parseInt()解析像八进制字面量的字符串时,ECMAScript 3和 5存在分歧。例如:
//ECMAScript 3 认为是 56(八进制),ECMAScript 5 认为是 70(十进制) var num = parseInt("070");
在 ECMAScript 3 JavaScript引擎中,"070"被当成八进制字面量,因此转换后的值是十进制的 56。 而在 ECMAScript 5 JavaScript引擎中,parseInt()已经不具有解析八进制值的能力,因此前导的零会 被认为无效,从而将这个值当成"70",结果就得到十进制的 70。在 ECMAScript 5中,即使是在非严格 模式下也会如此。 为了消除在使用 parseInt()函数时可能导致的上述困惑,可以为这个函数提供第二个参数:转换 时使用的基数(即多少进制)。如果知道要解析的值是十六进制格式的字符串,那么指定基数 16作为第 二个参数,可以保证得到正确的结果,例如:
var num = parseInt("0xAF", 16); //175
不指定基数意味着让 parseInt()决定如何解析输入的字符串,因此为了避免错误的解析,我们建 议无论在什么情况下都明确指定基数。 多数情况下,我们要解析的都是十进制数值,因此始终将 10 作为第二个参数是 非常必要的。
第三个函数parseFloat()也是从第一个字符(位置 0)开始解析每个字符。而且 也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。也就是说,字符串中的第 一个小数点是有效的,而第二个小数点就是无效的了,因此它后面的字符串将被忽略。举例来说, "22.34.5"将会被转换为 22.34。 除了第一个小数点有效之外,parseFloat()与 parseInt()的第二个区别在于它始终都会忽略前导 的零。parseFloat()可以识别前面讨论过的所有浮点数值格式,也包括十进制整数格式。但十六进制格 式的字符串则始终会被转换成 0。由于 parseFloat()只解析十进制值,因此它没有用第二个参数指定基 数的用法。后还要注意一点:如果字符串包含的是一个可解析为整数的数(没有小数点,或者小数点后 都是零),parseFloat()会返回整数。以下是使用 parseFloat()转换数值的几个典型示例。
var num1 = parseFloat("1234blue"); //1234 (整数) var num2 = parseFloat("0xA"); //0 var num3 = parseFloat("22.5"); //22.5 var num4 = parseFloat("22.34.5"); //22.34 var num5 = parseFloat("0908.5"); //908.5 var num6 = parseFloat("3.125e7"); //31250000
3.4.6String类型
String 类型用于表示由零或多个 16位 Unicode字符组成的字符序列,即字符串。字符串可以由双 引号(")或单引号(')表示,用双引号表示的字符串和用单引号表示的字符串完全相同。不过,以双引号开头的字符串也必须以双引号结尾,而以单引号开头的字符串必须以单引号结尾。
字符字面量:String 数据类型包含一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或者具有其 他用途的字符,如 “\r” 回车 "\n"换行 \b空格;任何字符串的长度都可以通过访问其 length 属性取得。
字符串的特点 :ECMAScript中的字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。要改变 某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量, 例如:
var lang = "Java"; lang = lang + "Script";
以上示例中的变量lang 开始时包含字符串"Java"。而第二行代码把lang 的值重新定义为"Java" 与"Script"的组合即"JavaScript"。实现这个操作的过程如下:首先创建一个能容纳 10个字符的 新字符串,然后在这个字符串中填充"Java"和"Script",后一步是销毁原来的字符串"Java"和字 符串"Script",因为这两个字符串已经没用了。这个过程是在后台发生的,而这也是在某些旧版本的
浏览器(例如版本低于 1.0的 Firefox、IE6等)中拼接字符串时速度很慢的原因所在。但这些浏览器后 来的版本已经解决了这个低效率问题。
转换为字符串 :要把一个值转换为一个字符串有两种方式。(toString()方法 和 转型函数String())
第一种是使用几乎每个值都有的 toString()方法。这个方法唯一要做的就是返回相应值的字符串表现。来看下面的例子:
var age = 11;
var ageAsString = age.toString(); // 字符串"11"
var found = true;
var foundAsString = found.toString(); // 字符串"true"
数值、布尔值、对象和字符串值(没错,每个字符串也都有一个 toString()方法,该方法返回字 符串的一个副本)都有 toString()方法。但 null 和 undefined 值没有这个方法。 多数情况下,调用 toString()方法不必传递参数。但是,在调用数值的 toString()方法时,可 以传递一个参数:输出数值的基数。默认情况下,toString()方法以十进制格式返回数值的字符串表 示。而通过传递基数,toString()可以输出以二进制、八进制、十六进制,乃至其他任意有效进制格 式表示的字符串值。下面给出几个例子:
var num = 10; alert(num.toString()); // "10"
alert(num.toString(2)); // "1010"
alert(num.toString(8)); // "12"
alert(num.toString(10)); // "10"
alert(num.toString(16)); // "a"
通过指定基数,toString()方法会改变输出的值。而数值 10根据基数的 不同,可以在输出时被转换为不同的数值格式。注意,默认的(没有参数的)输出值与指定基数 10 时 的输出值相同。
第二种方法转型函数String()在不知道要转换的值是不是 null 或 undefined 的情况下,还可以使用转型函数 String(),这个 函数能够将任何类型的值转换为字符串。String()函数遵循下列转换规则:
如果值有 toString()方法,则调用该方法(没有参数)并返回相应的结果;
如果值是 null,则返回"null";
如果值是 undefined,则返回"undefined"。 下面再看几个例子:
var value1 = 10;
var value2 = true;
var value3 = null;
var value4;
alert(String(value1)); // "10"
alert(String(value2)); // "true"
alert(String(value3)); // "null"
alert(String(value4)); // "undefined"
3.4.7Object类型
ECMAScript中的对象其实就是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要创建 的对象类型的名称来创建。而创建 Object 类型的实例并为其添加属性和(或)方法,就可以创建自定 义对象,如下所示:
var o = new Object(); 在 ECMAScript中,如果不给构造函数传递参数,则可 以省略后面的那一对圆括号。也就是说,在像前面这个示例一样不传递参数的情况下,完全可以省略那 对圆括号(但这不是推荐的做法):
var o = new Object; // 有效,但不推荐省略圆括号
仅仅创建 Object 的实例并没有什么用处,但关键是要理解一个重要的思想:即在 ECMAScript中, (就像 Java 中的 java.lang.Object 对象一样)Object 类型是所有它的实例的基础。换句话说, Object 类型所具有的任何属性和方法也同样存在于更具体的对象中。
Object 的每个实例都具有下列属性和方法。
constructor:保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor) 就是 Object()。
hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例 的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例 如:o.hasOwnProperty("name"))。
isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型(第 5 章将讨论原 型) 。
propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用 for-in 语句 (本章后面将会讨论)来枚举。与 hasOwnProperty()方法一样,作为参数的属性名必须以字符 串形式指定。
toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
toString():返回对象的字符串表示。
valueOf():返回对象的字符串、数值或布尔值表示。通常与 toString()方法的返回值 相同。 由于在 ECMAScript中 Object 是所有对象的基础,因此所有对象都具有这些基本的属性和方法。 第 5章和第 6章将详细介绍 Object 与其他对象的关系。
从技术角度讲,ECMA-262中对象的行为不一定适用于 JavaScript中的其他对象。 浏览器环境中的对象,比如 BOM 和 DOM 中的对象,都属于宿主对象,因为它们是 由宿主实现提供和定义的。ECMA-262不负责定义宿主对象,因此宿主对象可能会也 可能不会继承 Object。
3.5操作符
包括算术操作符(如加号和减号)、位操作符、 关系操作符和相等操作符。ECMAScript 操作符的与众不同之处在于,它们能够适用于很多值,例如字 符串、数字值、布尔值,甚至对象。不过,在应用于对象时,相应的操作符通常都会调用对象的 valueOf() 和(或)toString()方法,以便取得可以操作的值。
3.5.1自增自减
前置递增++i,前置递减--i:先+1(-1)在进行运算
后置递增i++;后置递减i--:先进行运算再+1(-1)
所有这 4个操作符对任何值都适用,也就是它们不仅适用于整数,还可以用于字符串、布尔值、浮 点数值和对象。在应用于不同的值时,递增和递减操作符遵循下列规则。
在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减 1 的操作。字 符串变量变成数值变量。
在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN(第 4章将详细讨论)。 字符串变量变成数值变量。 在应用于布尔值 false 时,先将其转换为 0再执行加减 1的操作。布尔值变量变成数值变量。
在应用于布尔值 true 时,先将其转换为 1再执行加减 1的操作。布尔值变量变成数值变量。
在应用于浮点数值时,执行加减 1的操作。
在应用于对象时,先调用对象的 valueOf()方法(第 5章将详细讨论)以取得一个可供操作的 值。然后对该值应用前述规则。如果结果是 NaN,则在调用 toString()方法后再应用前述规 则。对象变量变成数值变量。
3.5.2 一元加和减操作符
+ - 表示正负而已
一元加和减操作符主要用于基本的算术运算,也可以像前面示例所展示的一样用于转换数据类型。
3.5.3位操作符
在 ECMAScript中,当对数值应用位操作符时,后台会发生如下转换过程:64位的数值被转换成 32 位数值,然后执行位操作,后再将 32位的结果转换回 64位数值。这样,表面上看起来就好像是在操 作 32 位数值,就跟在其他语言中以类似方式执行二进制操作一样。但这个转换过程也导致了一个严重 的副效应,即在对特殊的 NaN 和 Infinity 值应用位操作时,这两个值都会被当成 0来处理。 如果对非数值应用位操作符,会先使用 Number()函数将该值转换为一个数值(自动完成),然后 再应用位操作。得到的结果将是一个数值。
1. 按位非(NOT) 按位非操作符由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码。按位非是 ECMAScript操作符中少数几个与二进制计算有关的操作符之一。下面看一个例子:
var num1 = 25; // 二进制 00000000000000000000000000011001
var num2 = ~num1; // 二进制 11111111111111111111111111100110 alert(num2); // -26
这里,对 25执行按位非操作,结果得到了26。这也验证了按位非操作的本质:操作数的负值减 1。 因此,下面的代码也能得到相同的结果:
var num1 = 25; var num2 = -num1 - 1; alert(num2); // "-26"
虽然以上代码也能返回同样的结果,但由于按位非是在数值表示的底层执行操作,因此速度更快。
2. 按位与(AND) 按位与操作符由一个和号字符(&)表示,它有两个操作符数。从本质上讲,按位与操作就是将两 个数值的每一位对齐,然后根据下表中的规则,对相同位置上的两个数执行 AND操作:
第一个数值的位 第二个数值的位 结 果
1 1 1 1 0 0 0 1 0 0 0 0
简而言之,按位与操作只在两个数值的对应位都是 1时才返回 1,任何一位是 0,结果都是 0。 下面看一个对 25和 3执行按位与操作的例子:
var result = 25 & 3; alert(result); //1
BitwiseAndExample01.htm 可见,对 25和 3执行按位与操作的结果是 1。为什么呢?请看其底层操作:
25 = 0000 0000 0000 0000 0000 0000 0001 1001 3 = 0000 0000 0000 0000 0000 0000 0000 0011 --------------------------------------------- AND = 0000 0000 0000 0000 0000 0000 0000 0001
原来,25和 3的二进制码对应位上只有一位同时是 1,而其他位的结果自然都是 0,因此终结果 等于 1。
3. 按位或(OR) 按位或操作符由一个竖线符号(|)表示,同样也有两个操作数。按位或操作遵循下面这个真值表。
第一个数值的位 第二个数值的位 结 果
1 1 1 1 0 1 0 1 1 0 0 0
由此可见,按位或操作在有一个位是1的情况下就返回1,而只有在两个位都是0的情况下才返回 0。
如果在前面按位与的例子中对 25和 3执行按位或操作,则代码如下所示:
var result = 25 | 3; alert(result); //27
25与 3按位或的结果是 27:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011 --------------------------------------------
OR =0000 0000 0000 0000 0000 0000 0001 1011
这两个数值的都包含 4个1,因此可以把每个 1直接放到结果中。二进制码 11011等于十进制值 27。
4. 按位异或(XOR) 按位异或操作符由一个插入符号(^)表示,也有两个操作数。以下是按位异或的真值表。
第一个数值的位 第二个数值的位 结 果
1 1 0 1 0 1 0 1 1 0 0 0
按位异或与按位或的不同之处在于,这个操作在两个数值对应位上只有一个 1时才返回 1,如果对 应的两位都是 1或都是 0,则返回 0。 对 25和 3执行按位异或操作的代码如下所示:
var result = 25 ^ 3; alert(result); //26
BitwiseXorExample01.htm
25与 3按位异或的结果是 26,其底层操作如下所示:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011 ---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010
5. 左移 左移操作符由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。
例如, 如果将数值 2(二进制码为 10)向左移动 5位,结果就是 64(二进制码为 1000000),代码如下所示:
var oldValue = 2; // 等于二进制的 10
var newValue = oldValue << 5; // 等于二进制的 1000000,十进制的 64
注意,在向左移位后,原数值的右侧多出了 5个空位。左移操作会以 0来填充这些空位,以便得到 的结果是一个完整的 32位二进制数(见图 3-2)。
图 3-2 注意,左移不会影响操作数的符号位。换句话说,如果将2向左移动5位,结果将是64,而非 64。
6. 有符号的右移 有符号的右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位(即 正负号标记)。有符号的右移操作与左移操作恰好相反,即如果将 64向右移动 5位,结果将变回 2:
var oldValue = 64; // 等于二进制的 1000000 var newValue = oldValue >> 5; // 等于二进制的 10 ,即十进制的 2
SignedRightShiftExample01.htm
同样,在移位过程中,原数值中也会出现空位。只不过这次的空位出现在原数值的左侧、符号位的 右侧(见图 3-3)。而此时 ECMAScript会用符号位的值来填充所有空位,以便得到一个完整的值。
7. 无符号右移 无符号右移操作符由 3个大于号(>>>)表示,这个操作符会将数值的所有 32位都向右移动。对正 数来说,无符号右移的结果与有符号右移相同。仍以前面有符号右移的代码为例,如果将 64 无符号右 移 5位,结果仍然还是 2:
var oldValue = 64; // 等于二进制的 1000000 var newValue = oldValue >>> 5; // 等于二进制的 10 ,即十进制的 2
UnsignedRightShiftExample01.htm
但是对负数来说,情况就不一样了。首先,无符号右移是以 0来填充空位,而不是像有符号右移那 样以符号位的值来填充空位。所以,对正数的无符号右移与有符号右移结果相同,但对负数的结果就不一样了。其次,无符号右移操作符会把负数的二进制码当成正数的二进制码。而且,由于负数以其绝对 值的二进制补码形式表示,因此就会导致无符号右移后的结果非常之大,如下面的例子所示:
var oldValue = -64; // 等于二进制的 (-64的补码)11111111111111111111111111000000
var newValue = oldValue >>> 5; // 等于十进制的 134217726 00000111111111111111111111111110
这里,当对64 执行无符号右移 5 位的操作后,得到的结果是 134217726。之所以结果如此之大, 是因为64的二进制码为 11111111111111111111111111000000,而且无符号右移操作会把这个二进制码当 成正数的二进制码,换算成十进制就是 4294967232。如果把这个值右移 5 位,结果就变成了 00000111111111111111111111111110,即十进制的 134217726。
3.5.4布尔操作符
布尔操作符一共有 3个:非(NOT)、与(AND) 和或(OR)。
1. 逻辑非 逻辑非操作符由一个叹号(!)表示,可以应用于 ECMAScript中的任何值。无论这个值是什么数据 类型,这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再 对其求反。也就是说,逻辑非操作符遵循下列规则:
如果操作数是一个对象,返回 false;
如果操作数是一个空字符串,返回 true;
如果操作数是一个非空字符串,返回 false;
如果操作数是数值 0,返回 true;
如果操作数是任意非 0数值(包括 Infinity),返回 false;
如果操作数是 null,返回 true;
如果操作数是 NaN,返回 true;
如果操作数是 undefined,返回 true。
"!!" 相当于 Number()函数:逻辑非操作符也可以用于将一个值转换为与其对应的布尔值。而同时使用两个逻辑非操作符,实际 上就会模拟 Boolean()转型函数的行为。其中,第一个逻辑非操作会基于无论什么操作数返回一个布 尔值,而第二个逻辑非操作则对该布尔值求反,于是就得到了这个值真正对应的布尔值。当然,终结 果与对这个值使用 Boolean()函数相同,
逻辑与操作符由两个和号(&&)表示,有两个操作数,如下面的例子所示:
var result = true && false;
2.逻辑与
逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况 下,逻辑与操作就不一定返回布尔值;此时,它遵循下列规则:
如果第一个操作数是对象,则返回第二个操作数;
如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该 对象;
如果两个操作数都是对象,则返回第二个操作数;
如果有一个操作数是 null,则返回 null;
如果有一个操作数是 NaN,则返回 NaN;
逻辑与操作属于短路操作,即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。 对于逻辑与操作而言,如果第一个操作数是 false,则无论第二个操作数是什么值,结果都不再可能是 true 了
如果有一个操作数是 undefined,则返回 undefined。
3. 逻辑或
逻辑或操作符由两个竖线符号(||)表示,有两个操作数。与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值;此时,它遵循下 列规则:
如果第一个操作数是对象,则返回第一个操作数;
如果第一个操作数的求值结果为 false,则返回第二个操作数;
如果两个操作数都是对象,则返回第一个操作数;
如果两个操作数都是 null,则返回 null;
如果两个操作数都是 NaN,则返回 NaN;
如果两个操作数都是 undefined,则返回 undefined。
与逻辑与操作符相似,逻辑或操作符也是短路操作符。也就是说,如果第一个操作数的求值结果为 true,就不会对第二个操作数求值了。
3.5.5 乘性操作符
ECMAScript定义了 3个乘性操作符:乘法、除法和求模。在操作数为非数值的情况下会执行自动的类型转换。如果参与乘性计算的某 个操作数不是数值,后台会先使用 Number()转型函数将其转换为数值。也就是说,空字符串将被当作 0,布尔值 true 将被当作 1。 对于这三个操作符,只要有一个操作数是NaN,结果就是NaN;
1. 乘法 :乘法操作符由一个星号(*)表示,用于计算两个数值的乘积。
在处理特殊值的情况下,乘法操作符遵循下列特殊的规则:
如果操作数都是数值,执行常规的乘法计算,即两个正数或两个负数相乘的结果还是正数,而 如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了 ECMAScript数值的表示范围, 则返回 Infinity 或-Infinity;
如果有一个操作数是 NaN,则结果是 NaN;
如果是 Infinity 与 0相乘,则结果是 NaN;
如果是 Infinity 与非 0数值相乘,则结果是 Infinity 或-Infinity,取决于有符号操作数 的符号;
如果是 Infinity 与 Infinity 相乘,则结果是 Infinity;
如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的 规则。
2. 除法:除法操作符由一个斜线符号(/)表示,执行第二个操作数除第一个操作数的计算,
与乘法操作符类似,除法操作符对特殊的值也有特殊的处理规则。这些规则如下:
如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而 如果只有一个操作数有符号,那么结果就是负数。如果商超过了 ECMAScript数值的表示范围, 则返回 Infinity 或-Infinity;
如果有一个操作数是 NaN,则结果是 NaN;
如果是 Infinity 被 Infinity 除,则结果是 NaN;
如果是零被零除,则结果是 NaN;
如果是非零的有限数被零除,则结果是 Infinity 或-Infinity,取决于有符号操作数的符号;
如果是 Infinity 被任何非零数值除,则结果是 Infinity 或-Infinity,取决于有符号操作 数的符号;
如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则。
3. 求模 :求模(余数)操作符由一个百分号(%)表示,用法如下:
与另外两个乘性操作符类似,求模操作符会遵循下列特殊规则来处理特殊的值:
如果操作数都是数值,执行常规的除法计算,返回除得的余数;
如果被除数是无穷大值而除数是有限大的数值,则结果是 NaN;
如果被除数是有限大的数值而除数是零,则结果是 NaN;
如果是 Infinity 被 Infinity 除,则结果是 NaN;
如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数;
如果被除数是零,则结果是零;
如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则。
3.5.6 加性操作符
加法和减法这两个加性操作符应该说是编程语言中简单的算术操作符了。但是在 ECMAScript中, 这两个操作符却都有一系列的特殊行为。与乘性操作符类似,加性操作符也会在后台转换不同的数据类 型。对于这两个操作符,只要有一个操作数是NaN,结果就是NaN。
1.加法 (+)如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:
如果有一个操作数是 NaN,则结果是 NaN;
如果是 Infinity 加 Infinity,则结果是 Infinity;
如果是-Infinity 加-Infinity,则结果是-Infinity;
如果是 Infinity 加-Infinity,则结果是 NaN;
如果是+0加+0,则结果是+0;
如果是-0加-0,则结果是-0;
如果是+0加-0,则结果是+0。
不过,如果有一个操作数是字符串,那么就要应用如下规则:
如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接 起来。 如果有一个操作数是对象、数值或布尔值,则调用它们的 toString()方法取得相应的字符串值, 然后再应用前面关于字符串的规则。对于 undefined 和 null,则分别调用 String()函数并取得字符 串"undefined"和"null"。
2.减法(-)
与加法操作符类似,ECMAScript 中的减法操作符在处理各种数据类型转换时,同样需要遵循一些 特殊规则,如下所示:
如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
如果有一个操作数是 NaN,则结果是 NaN;
如果是 Infinity 减 Infinity,则结果是 NaN;
如果是-Infinity 减-Infinity,则结果是 NaN;
如果是 Infinity 减-Infinity,则结果是 Infinity;
如果是-Infinity 减 Infinity,则结果是-Infinity;
如果是+0减+0,则结果是+0;
如果是+0减0,则结果是0;
如果是0减0,则结果是+0;
如果有一个操作数是字符串、布尔值、null 或 undefined,则先在后台调用 Number()函数将 其转换为数值,然后再根据前面的规则执行减法计算。如果转换的结果是 NaN,则减法的结果 就是 NaN;
如果有一个操作数是对象,则调用对象的 valueOf()方法以取得表示该对象的数值。如果得到 的值是 NaN,则减法的结果就是 NaN。如果对象没有 valueOf()方法,则调用其 toString() 方法并将得到的字符串转换为数值。
3.5.7 关系操作符
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比 较,。这几个操作符都返回一个布尔值,与 ECMAScript中的其他操作符一样,当关系操作符的操作数使用了非数值时,也要进行数据转换 或完成某些奇怪的操作。以下就是相应的规则。 (根据规则,任何操作数包括NaN自己,与 NaN 进行 关系比较,结果都是 false。)
如果两个操作数都是数值,则执行数值比较。
如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。A65 a97;例如var result = "Brick" < "alphabet"; //true
如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
如果一个操作数是对象,则调用这个对象的 valueOf()方法,用得到的结果按照前面的规则执 行比较。如果对象没有 valueOf()方法,则调用 toString()方法,并用得到的结果根据前面 的规则执行比较。
如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。 在使用关系操作符比较两个字符串时,会执行一种奇怪的操作。
3.5.8 相等操作符
确定两个变量是否相等是编程中的一个非常重要的操作。在比较字符串、数值和布尔值的相等性时, 问题还比较简单。但在涉及到对象的比较时,问题就变得复杂了。早的 ECMAScript中的相等和不等 操作符会在执行比较之前,先将对象转换成相似的类型。后来,有人提出了这种转换到底是否合理的质 疑。后,ECMAScript 的解决方案就是提供两组操作符:相等和不相等——先转换再比较,全等和不 全等——仅比较而不转换。
1. 相等和不相等 ECMAScript中的相等操作符由两个等于号(==)表示,如果两个操作数相等,则返回 true。而不 相等操作符由叹号后跟等于号(!=)表示,如果两个操作数不相等,则返回 true。这两个操作符都会 先转换操作数(通常称为强制转型),然后再比较它们的相等性。 在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则: 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为 0,而 true 转换为 1;
如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法,用得到的基本类 型值按照前面的规则进行比较; 这两个操作符在进行比较时则要遵循下列规则。
null 和 undefined 是相等的。
要比较相等性之前,不能将 null 和 undefined 转换成其他任何值。
如果有一个操作数是 NaN,则相等操作符返回 false,而不相等操作符返回 true。重要提示: 即使两个操作数都是 NaN,相等操作符也返回 false;因为按照规则,NaN 不等于 NaN。
如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象, 则相等操作符返回 true;否则,返回 false。 下表列出了一些特殊情况及比较结果:
表 达 式 值 表 达 式 值 null == undefined true true == 1 true "NaN" == NaN false true == 2 false 5 == NaN false undefined == 0 false NaN == NaN false null == 0 false NaN != NaN true "5"==5 true false == 0 true
2. 全等和不全等
除了在比较之前不转换操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。全 等操作符由 3个等于号(===)表示,它只在两个操作数未经转换就相等的情况下返回 true, 记住:null == undefined 会返回 true,因为它们是类似的值;但 null === undefined 会返回 false,因为它们是不同类型的值。
由于相等和不相等操作符存在类型转换问题,而为了保持代码中数据类型的完整 性,我们推荐使用全等和不全等操作符。
3.5.9 条件操作符
条件操作符应该算是 ECMAScript中灵活的一种操作符了,而且它遵循与 Java中的条件操作符相 同的语法形式,如下面的例子所示: variable = boolean_expression ? true_value : false_value;
本质上,这行代码的含义就是基于对 boolean_expression 求值的结果,决定给变量 variable 赋什么值。如果求值结果为 true,则给变量 variable 赋 true_value 值;如果求值结果为 false, 则给变量 variable 赋 false_value 值。
3.5.10 赋值操作符
简单的赋值操作符由等于号(=)表示,其作用就是把右侧的值赋给左侧的变量,如下面的例子所示:
var num = 10;
如果在等于号(=)前面再添加乘性操作符、加性操作符或位操作符,就可以完成复合赋值操作。 这种复合赋值操作相当于是对下面常规表达式的简写形式:
每个主要算术操作符(以及个别的其他操作符)都有对应的复合赋值操作符。这些操作符如下所示:
乘/赋值(*=); 除/赋值(/=); 模/赋值(%=); 加/赋值(+=); 减/赋值(=); 左移/赋值(<<=); 有符号右移/赋值(>>=);
无符号右移/赋值(>>>=)。 设计这些操作符的主要目的就是简化赋值操作。使用它们不会带来任何性能的提升。
3.5.11 逗号操作符
使用逗号操作符可以在一条语句中执行多个操作,如下面的例子所示:
var num1=1, num2=2, num3=3;
逗号操作符多用于声明多个变量;但除此之外,逗号操作符还可以用于赋值。在用于赋值时,逗号 操作符总会返回表达式中的后一项,如下面的例子所示:
var num = (5, 1, 4, 8, 0); // num 的值为 0
由于 0是表达式中的后一项,因此 num 的值就是 0。虽然逗号的这种使用方式并不常见,但这个 例子可以帮我们理解逗号的这种行为。
3.6 语句
3.6.1 if语句
大多数编程语言中为常用的一个语句就是 if 语句。以下是 if 语句的语法: if (condition) statement1 else statement2
其中的 condition(条件)可以是任意表达式;而且对这个表达式求值的结果不一定是布尔值。 ECMAScript会自动调用 Boolean()转换函数将这个表达式的结果转换为一个布尔值。
如果对 condition 求值的结果是true,则执行statement1(语句1),如果对condition求值的结果是false,则执行statement2 (语句 2)。
3.6.2 do-while语句
do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。 换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次。以下是 do-while 语句的 语法: do { statement } while (expression);
下面是一个示例:
var i = 0; do { i += 2; } while (i < 10); alert(i);
像 do-while 这种后测试循环语句最常用于循环体中的代码至少要被执行一次的 情形。
3.6.3 while语句
while 语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。 因此,循环体内的代码有可能永远不会被执行。以下是 while 语句的语法: while(expression) statement
下面是一个示例:
var i = 0; while (i < 10) {
i += 2; }
3.6.4 for语句
for 语句也是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代 码的能力。以下是 for 语句的语法: for (initialization; expression; post-loop-expression) statement
下面是一个示例:
var count = 10; for (var i = 0; i < count; i++){ alert(i); }
ForStatementExample01.htm 以上代码定义了变量 i 的初始值为 0。只有当条件表达式(i<count)返回 true 的情况下才会进 入 for 循环,因此也有可能不会执行循环体中的代码。如果执行了循环体中的代码,则一定会对循环后 的表达式(i++)求值,即递增 i 的值。
将这三个表达式全部 省略,就会创建一个无限循环,例如: for (;;) { // 无限循环 doSomething(); }
3.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-in 循环输出的属性名的顺序是不可预测的。 具体来讲,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。 但是,如果表示要迭代的对象的变量值为 null 或 undefined,for-in 语句会抛出错误。 ECMAScript 5更正了这一行为;对这种情况不再抛出错误,而只是不执行循环体。为了保证大限度的兼容性,建议在使用 for-in 循环之前,先检测确认该对象的值不是 null 或 undefined。
3.6.6 label语句
使用 label 语句可以在代码中添加标签,以便将来使用。以下是 label 语句的语法: label: statement
经常用在嵌套循环中,配合continue和break指定循环出口的位置。
参考https://blog.csdn.net/creabine/article/details/62228145
3.6.7 break和continue语句
break 和 continue 语句用于在循环中精确地控制代码的执行。
其中,break 语句会立即退出循环, 强制继续执行循环后面的语句。即刻退出所在的循环。
而 continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶 部继续执行。仅仅是少执行一次循环语句。
3.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 语句,否则将视为语法错误。
由于大量使用 with 语句会导致性能下降,同时也会给调试代码造成困难,因此 在开发大型应用程序时,不建议使用 with 语句。
3.6.9 switch语句
switch 语句与 if 语句的关系为密切,而且也是在其他语言中普遍使用的一种流控制语句。
虽然 ECMAScript中的 switch 语句借鉴自其他语言,但这个语句也有自己的特色。
首先,可以在 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 语句使用的就是字符串。其中,第一种情形实际上是一个对字符串拼接操 作求值的表达式。由于这个字符串拼接表达式的结果与 switch 的参数相等,因此结果就会显示 "Greeting was found."。
switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换(例如, 字符串"10"不等于数值 10)。
3.7 函数
函数对任何语言来说都是一个核心的概念。ECMAScript中的函数使用 function 关键字来声明,后跟一组参数以及函数体。 函数的基本语法如下所示:
function functionName(arg0, arg1,...,argN) { statements }
这个函数可以通过其函数名来调用,后面还要加上一对圆括号和参数(圆括号中的参数如果有多个, 可以用逗号隔开)。函数中定义中的命名参数 name 和 message 被用作了字符串拼接的两个操作数,而结果终通过警告框显示了出来。 ECMAScript 中的函数在定义时不必指定是否返回值。实际上,任何函数在任何时候都可以通过 return 语句后跟要返回的值来实现返回值。位于 return 语句之后的任何代码 都永远不会执行。例如:
function sum(num1, num2) {
return num1 + num2;
alert("Hello world"); // 永远不会执行
}
另外,return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回 undefined 值。这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下。
推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。否则,如 果函数有时候返回值,有时候有不返回值,会给调试代码带来不便。
严格模式对函数有一些限制:
不能把函数命名为 eval 或 arguments;
不能把参数命名为 eval 或 arguments;
不能出现两个命名参数同名的情况。
如果发生以上情况,就会导致语法错误,代码无法执行。
3.7.1 理解参数
ECMAScript函数的参数与大多数其他语言中函数的参数有所不同。ECMAScript函数不介意传递进 来多少个参数,也不在乎传进来参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数, 在调用这个函数时也未必一定要传递两个参数。可以传递一个、三个甚至不传递参数,而解析器永远不 会有什么怨言。之所以会这样,原因是 ECMAScript中的参数在内部是用一个数组来表示的。函数接收 到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任 何元素,无所谓;如果包含多个元素,也没有问题。实际上,在函数体内可以通过 arguments 对象来 访问这个参数数组,从而获取传递给函数的每一个参数。 其实,arguments 对象只是与数组类似(它并不是 Array 的实例),因为可以使用方括号语法访 问它的每一个元素(即第一个元素是 arguments[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
执行以上代码会依次出现 3个警告框,分别显示 2、0和 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
虽然这个特性算不上完 美的重载,但也足够弥补 ECMAScript的这一缺憾了。
另一个与参数相关的重要方面,就是 arguments 对象可以与命名参数一起使用,如下面的例子所示:
function doAdd(num1, num2) {
if(arguments.length == 1) {
alert(num1 + 10); }
else if (arguments.length == 2) {
alert(arguments[0] + num2); }
}
在重写后的这个 doAdd()函数中,两个命名参数都与 arguments 对象一起使用。由于 num1 的值 与 arguments[0]的值相同,因此它们可以互换使用(当然,num2 和 arguments[1]也是如此)。 关于 arguments 的行为,还有一点比较有意思。那就是它的值永远与对应命名参数的值保持同步。 例如:
function doAdd(num1, num2) {
arguments[1] = 10;
alert(arguments[0] + num2);
}
每次执行这个 doAdd()函数都会重写第二个参数,将第二个参数的值修改为 10。因为 arguments 对象中的值会自动反映到对应的命名参数,所以修改 arguments[1],也就修改了 num2,结果它们的 值都会变成 10。
不过,这并不是说读取这两个值会访问相同的内存空间;它们的内存空间是独立的,但 它们的值会同步。
另外还要记住,如果只传入了一个参数,那么为 arguments[1]设置的值不会反应到 命名参数中。这是因为 arguments 对象的长度是由传入的参数个数决定的,不是由定义函数时的命名参数(形参)的个数决定的。
关于参数还要记住后一点:没有传递值的命名参数将自动被赋予 undefined 值。这就跟定义了 变量但又没有初始化一样。例如,如果只给 doAdd()函数传递了一个参数,则 num2 中就会保存 undefined 值。 严格模式对如何使用 arguments 对象做出了一些限制。首先,像前面例子中那样的赋值会变得无 效。也就是说,即使把 arguments[1]设置为 10,num2 的值仍然还是 undefined。其次,重写 arguments 的值会导致语法错误(代码将不会执行)。
ECMAScript中的所有参数传递的都是值,不可能通过引用传递参数。
3.7.2 没有重载
ECMAScript 函数不能像传统意义上那样实现重载。而在其他语言(如 Java)中,可以为一个函数 编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。如前所述,ECMAScirpt 函数没有签名,因为其参数是由包含零或多个值的数组来表示的。而没有函数签名,真正的重载是不可 能做到的。 如果在 ECMAScript中定义了两个名字相同的函数,则该名字只属于后定义的函数。由于后定义的函数覆盖了先定义的函数.
3.8 小结
JavaScript 的核心语言特性在 ECMA-262 中是以名为 ECMAScript 的伪语言的形式来定义的。 ECMAScript 中包含了所有基本的语法、操作符、数据类型以及完成基本的计算任务所必需的对象,但 没有对取得输入和产生输出的机制作出规定。理解 ECMAScript及其纷繁复杂的各种细节,是理解其在 Web 浏览器中的实现——JavaScript的关键。目前大多数实现所遵循的都是 ECMA-262第 3版,但很多 也已经着手开始实现第 5版了。
以下简要总结了 ECMAScript中基本的要素。
ECMAScript中的基本数据类型包括 Undefined、Null、Boolean、Number 和 String。
与其他语言不同,ECMScript没有为整数和浮点数值分别定义不同的数据类型,Number 类型可 用于表示所有数值。
ECMAScript中也有一种复杂的数据类型,即 Object 类型,该类型是这门语言中所有对象的基 础类型。
严格模式为这门语言中容易出错的地方施加了限制。
ECMAScript提供了很多与 C及其他类 C语言中相同的基本操作符,包括算术操作符、布尔操作 符、关系操作符、相等操作符及赋值操作符等。
ECMAScript从其他语言中借鉴了很多流控制语句,例如 if 语句、for 语句和 switch 语句等。 ECMAScript中的函数与其他语言中的函数有诸多不同之处。
无须指定函数的返回值,因为任何 ECMAScript函数都可以在任何时候返回任何值。
实际上,未指定返回值的函数返回的是一个特殊的 undefined 值。
ECMAScript中也没有函数签名的概念,因为其函数参数是以一个包含零或多个值的数组的形式 传递的。
可以向ECMAScript函数传递任意数量的参数,并且可以通过 arguments 对象来访问这些参数。
由于不存在函数签名的特性,ECMAScript函数不能重载。