JavaScript基本概念

语法

ECMAScript的语法大量借鉴了C及其他类C语言(如Java和Perl)的语法。

区分大小写

要理解的第一个概念就是ECMAScriptj中的一切(变量、函数名和操作符)都区分大小写。这也就意味着,变量名test和变量名Test分别表示两个不同的变量,而函数名不能使用typeof,因为它是一个关键字(3.2节介绍关键字),但typeOf则完全可以是一个有效的函数名。

标识符

所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符可以是按照下列格式规则组合起来的一或多个字符:
1. 第一个字符必须是一个字母、下划线(_)或一个美元符号($);
2. 其他字符可以是字母、下划线、美元符号或数字。

标识符中的字母也可以包含扩展的ASCII或Unicode字母字符(如A和Æ),但我们不推荐这样做。

按照惯例,ECMAScript标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个有意义的单词的首字母大写,例如:

firstSecond
myCar
doSomethingImportant

虽然没有谁强制要求必须采用这种格式,但为了与ECMAScript内置的函数和对象命名格式保持一致,可以将其当作一种最佳实践。

不能把关键字、保留字、true、false和null用作标识符。

注释

ECMAScript使用C风格的注释,包括单行注释和块级注释。单行注释以两个斜杠开头,如下所示:

//单行注释

块级注释以一个斜杠和一个星号(/)开头,以一个星号和一个斜杠(/)结尾,如下所示:

/*
 *这是一个块级注释
 */

虽然上面注释中的第二和第三行都以一个星号开头,但这不是必需的。之所以添加那两个星号,纯粹是为了提高注释的可读性(这种格式在企业级应用程序中极其常见)。

严格模式

ECMAScript 5引入了严格模式(strict mode)的概念。严格模式是为JavaScript定义了一种不同的解析与执行模型。在严格模式下,ECMAScript 3中的一些不确定的行为将得到处理,而且对某些不安全的操作也会抛出错误。要在整个脚本中启用严格模式,可以在顶部添加如下代码:

"use strict";

这行代码看起来像是字符串,而且也没有赋值给任何变量,但其实它是一个编译指示(pragma),用于告诉支持的JavaScript引擎切换到严格模式。这是为不破坏ECMAScript 3语法而特意选定的语法。

在函数内部的上方包含这条编译指示,也可以指定函数在严格模式下执行:

function doSomething() {
    "use strict;"
    //函数体
}

严格模式下,JavaScript的执行结果会有很大不同,因此本书将会随时指出严格模式下的区别。支持严格模式的浏览器包括IE10+、Firefox 4+、Safari 5.1+、Opera 12+和Chrome。

语句

ECMAScript中的语句以一个分号结尾;如果省略分号,则由解析器确定语句的结尾,如下例所示:

    var sum = a + b //正确,但不推荐
    var diff = a-b; //正确,k推荐

虽然语句结尾的分号不是必需的,但我们建议任何时候都不要省略它。因为加上这个分号可以避免很多错误(例如不完整的输入),开发人员也可以放心地通过删除多余的空格来压缩ECMAScript代码(代码行结尾处没有分号会导致压缩错误)。另外,加上分号也会在某些情况下增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了。

可以使用C风格的语法把多条语句组合到一个代码块中,即代码块以左花括号({)开头,以右花括号(})结尾:

if (test) {
    test =false;
    alert(test);
}

虽然条件控制语句(如if语句)只在执行多条语句的情况下才要求使用代码块,但最佳实践是始终在控制语句中使用代码块——即使代码块中只有一条语句,例如:

if (test) 
    alert(test); //有效,但不推荐
if (test) {
    alert(test); //有效,推荐
}

在控制语句中使用代码块可以让编码意图更加清晰,而且也能降低修改代码时出错的几率。

关键字和保留字

ECMA-262描述了一组具有特定用途的关键字,这些关键字可用于表示控制语句的开始或结束,或者用于执行特定操作等。按照规则,关键字也是语言保留的,不能用作标识符。以下就是ECMAScript的全部关键字(带*号上标的是第5版新增的关键字):

关键字关键字关键字关键字
breakdoinstanceoftypeof
caseelsenewvar
catchfinallyreturnvoid
continueforswitchwhile
debugger*functionthiswith
defaultifthrow
deleteintry

ECMA-262还描述了另外一组不能用作标识符的保留字。尽管保留字在这门语言中还没有任何特定的用途,但它们有可能在将来被用作关键字。以下是ECMA-262第3版定义的全部保留字:

保留字保留字保留字保留字
abstractenumintshort
booleanexportinterfacestatic
byteextendslongsuper
charfinalnativesynchronized
classfloatpackagethrows
constgotoprivatetransient
debuggerimplementsprotectedvolatile
doubleimportantpublicvolatile

第5版把在非严格模式下运行时的保留字缩减为下列这些:

保留字保留字保留字保留字
calssenumextendssuper
constexportimport

在严格模式下,第5版还对以下保留字施加了限制:

保留字保留字保留字
implementspackagepublic
interfaceprivatestatic
letprotectedyield

变量

ECMAScript的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说,每个变量仅仅是一个用于保存值的占位符而已。定义变量时要使用var操作符(注意var是一个关键字),后跟变量名(即一个标识符),如下所示:

var message;

这行代码定义了一个名为message的变量,该变量可以用来保存任何值(像这样未经过初始化的变量,会保存一个特殊的值——undefined,相关内容将在3.4节讨论)。ECMAScript也支持直接初始化变量,因此在定义变量的同时就可以设置变量的值,如下所示:

var message ="hi";

在此,变量message中保存了一个字符串值”hi”。像这样初始化变量并不会把它标记为字符串类型;初始化的过程就是给变量赋一个值那么简单。因此,可以在修改变量值的同时修改值的类型,如下所示:

var message = "hi";
message = 100;      //有效,不推荐

在这个例子中,变量message一开始保存了一个字符串值”hi”,然后该值又被一个数字值100取代。虽然我们不建议修改变量所保存值的类型,但这种操作在ECMAScript中完全有效。

有一点必须注意,即使用var操作符定义的变量将成为定义该变量的作用域中的局部变量。也就是说,如果在函数中使用var定义一个变量,那么这个变量在函数退出后就会被销毁,例如:

function test() {
    var message = "hi"; //局部变量
}
test();
alert(message); //错误!

这里,变量message是在函数中使用var定义的。当函数被调用时,就会创建该变量并为其赋值。而在此之后,这个变量又会立即被销毁,因此例子中的下一行代码就会导致错误。不过,可以像下面这样省略var操作符,从而创建一个全局变量:

function test() {
    message = "hi"; //全局变量
}
test();
alert(message); // "hi"

这个例子省略了var操作符,因而message就成了全局变量。这样,只要调用过一次test()函数,这个变量就有了定义,就可以在函数外部的任何地方被访问到。

虽然省略var操作符可以定义全局变量,但这也不是我们推荐的做法。因为在局部作用域中定义的全局变量很难维护,而且如果有意地忽略了var操作符,也会由于相应变量不会马上就有定义而导致不必要的混乱。给未经声明的变量赋值在严格模式下会导致抛出ReferenceError错误。
可以使用一条语句定义多个变量,只要像下面这样把每个变量(初始化或不初始化均可)用逗号分隔开即可:

var message = "hi",
    found = false,
    age = 29;

这个例子定义并初始化了3个变量。同样由于ECMAScript是松散类型的,因而使用不同类型初始化变量的操作可以放在一条语句中来完成。虽然代码里的换行和变量缩进不是必需的,但这样做可以提高可读性。

在严格模式下,不能定义名为eval或arguments的变量,否则会导致语法错误。

数据类型

ECMAScript中有5种简单数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number和String。还有1种复杂数据类型——Object,Object本质上是由一组无序的名值对组成的。ECMAScript不支持任何创建自定义类型的机制,而所有值最终都将是上述6种数据类型之一。乍一看,好像只有6种数据类型不足以表示所有数据;但是,由于ECMAScript数据类型具有动态性,因此的确没有再定义其他数据类型的必要了。

typeof操作符

鉴于ECMAScript是松散类型的,因此需要有一种手段来检测给定变量的数据类型——typeof就是负责提供这方面信息的操作符。对一个值使用typeof操作符可能返回下列某个字符串:
1. “undefined”——如果这个值未定义;
2. “boolean”——如果这个值是布尔值;
3. “string”——如果这个值是字符串;
4. “number”——如果这个值是数值;
5. “object”——如果这个值是对象或null;
6. “function”——如果这个值是函数。

有些时候,typeof操作符会返回一些令人迷惑但技术上却正确的值。比如,调用typeof null会返回”object”,因为特殊值null被认为是一个空的对象引用。Safari 5及之前版本、Chrome 7及之前版本在对正则表达式调用typeof操作符时会返回“function”,而其他浏览器在这种情况下会返回”object”。

从技术角度讲,函数在ECMAScript中是对象,不是一种数据类型。然而,函数也确实有一些特殊的属性,因此通过typeof操作符来区分函数和其他对象是有必要的。

Undefined类型

Undefined类型只有一个值,即特殊的undefined。在使用var声明变量但未对其加以初始化时,这个变量的值就是undefined,例如:

var message;
alert(message == undefined); //true

一般而言,不存在需要显式地把一个变量设置为undefined值的情况。字面值undefined的主要目的是用于比较,而ECMA-262第3版之前的版本中并没有规定这个值。第3版引入这个值是为了正式区分空对象指针与未经初始化的变量。
不过,包含undefined值的变量与尚未定义的变量还是不一样的。看看下面这个例子:

var message;
alert(message);        // "undefined"
alert(age);            // error
alert(typeof message); // "undefined"
alert(typeof age);     // "undefined"

结果表明,对未初始化和未声明的变量执行typeof操作符都返回了undefined值;这个结果有其逻辑上的合理性。因为虽然这两种变量从技术角度看有本质区别,但实际上无论对哪种变量也不可能执行真正的操作。

即便未初始化的变量会自动被赋予undefined值,但显式地初始化变量依然是明智的选择。如果能够做到这一点,那么当typeof操作符返回”undefined”值时,我们就知道被检测的变量还没有被声明,而不是尚未初始化。

Null类型

Null类型是第二个只有一个值的数据类型,这个特殊的值是null。从逻辑角度来看,null值表示一个空对象指针,而这也正是使用typeof操作符检测null值时会返回”object”的原因。

如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null而不是其他值。这样一来,只要直接检查null值就可以知道相应的变量是否已经保存了一个对象的引用了。

实际上,undefined值是派生自null值的,因此ECMA-262规定对它们的相等性测试要返回true。

alert(null == undefined); //true

尽管null和undefined有这样的关系,但它们的用途完全不同。如前所述,无论在什么情况下都没有必要把一个变量的值显式地设置为undefined,可是同样的规则对null却不适用。换句话说,只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存null值。这样做不仅可以体现null作为空对象指针的惯例,而且也有助于进一步区分null和undefined。

Boolean类型

Boolean类型是ECMAScript中使用得最多的一种类型,该类型只有两个字面值:true和false。这两个值与数字值不是一回事,因此true不一定等于1,而false也不一定等于0。

需要注意的是,Boolean类型的字面值true和false是区分大小写的。也就是说,True和False(以及其他的混合大小写形式)都不是Boolean值,只是标识符。

虽然Boolean类型的字面值只有两个,但ECMAScript中所有类型的值都有与这两个Boolean值等价的值。要将一个值转换为其对应的Boolean值,可以调用转型函数Boolean()。

可以对任何数据类型的值调用Boolean()函数,而且总会返回一个Boolean值。至于返回的这个值是true还是false,取决于要转换值的数据类型及其实际值。下表给出了各种数据类型及其对应的转换规则。

数据类型转换成true的值转换成false的值
Booleantruefalse
String任何非空字符串”和”“
Number任何非零数字值(包括无穷大)0和NaN
Object任何对象null
Undefinedn/aundefined

Number类型

Number类型应该是ECMAScript中最令人关注的数据类型了,这种类型使用IEEE754格式来表示整数和浮点数值(浮点数值在某些语言中也被称为双精度数值)。为支持各种数值类型,ECMA-262定义了不同的数值字面量格式。

最基本的数值字面量格式是十进制整数,十进制整数可以像下面这样直接在代码中输入:

var intNum = 55; // 整数

除了以十进制表示外,整数还可以通过八进制(以8为基数)或十六进制(以16为基数)的字面值来表示。其中,八进制字面值的第一位必须是零(0),然后是八进制数字序列(0~7)。如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析。请看下面的例子:

var octalNum1 = 070; //八进制的56
var octalNum2 = 079; //无效的八进制数值——解析为79 

八进制字面量在严格模式下是无效的,会导致支持的JavaScript引擎抛出错误。

十六进制字面值的前两位必须是0x,后跟任何十六进制数字(0~9及A~F)。其中,字母A~F可以大写,也可以小写。如下面的例子所示:

var hexNum1 = 0xA;  //十六进制的10
var hexNum2 = 0x1f; //十六进制的31

在进行算术计算时,所有以八进制和十六进制表示的数值最终都将被转换成十进制数值。

浮点数值

所谓浮点数值,就是该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。虽然小数点前面可以没有整数,但我们不推荐这种写法。

由于保存浮点数值需要的内存空间是保存整数值的两倍,因此ECMAScript会不失时机地将浮点数值转换为整数值。显然,如果小数点后面没有跟任何数字,那么这个数值就可以作为整数值来保存。同样地,如果浮点数值本身表示的就是一个整数(如1.0),那么该值也会被转换为整数,如下面的例子所示:

var floatNum1 = 1.;   //小数点后没有数字——解析为1
var floatNum2 = 10.0; //整数——解析为10

对于那些极大或极小的数值,可以用e表示法(即科学计数法)表示的浮点数值表示。

在默认情况下,ECMASctipt会将那些小数点后面带有6个零以上的浮点数值转换为以e表示法表示的数值(例如,0.0000003会被转换成3e-7)。

浮点数值的最高精度是17位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1加0.2的结果不是0.3,而是0.30000000000000004。这个小小的舍入误差会导致无法测试特定的浮点数值。

永远不要测试某个特定的浮点数值。

数值范围

由于内存的限制,ECMAScript并不能保存世界上所有的数值。ECMAScript能够表示的最小数值保存在Number.MIN_VALUE中——在大多数浏览器中,这个值是5e-324;能够表示的最大数值保存在Number.MAX_VALUE中——在大多数浏览器中,这个值是1.7976931348623157e+308。如果某次计算的结果得到了一个超出JavaScript数值范围的值,那么这个数值将被自动转换成特殊的Infinity值。具体来说,如果这个数值是负数,则会被转换成-Infinity(负无穷),如果这个数值是正数,则会被转换成Infinity(正无穷)。

如上所述,如果某次计算返回了正或负的Infinity值,那么该值将无法继续参与下一次的计算,因为Infinity不是能够参与计算的数值。要想确定一个数值是不是有穷的(换句话说,是不是位于最小和最大的数值之间),可以使用isFinite()函数。这个函数在参数位于最小与最大数值之间时会返回true。

尽管在计算中很少出现某些值超出表示范围的情况,但在执行极小或极大数值的计算时,检测监控这些值是可能的,也是必需的。
访问Number.NEGATIVE_INFINITY和Number.POSITIVE_INFINITY也可以得到负和正Infinity的值。可以想见,这两个属性中分别保存着-Infinity和Infinity。

NaN

NaN,即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。例如,在其他编程语言中,任何数值除以0都会导致错误,从而停止代码执行。但在ECMAScript中,任何数值除以0会返回NaN,因此不会影响其他代码的执行。

NaN本身有两个非同寻常的特点。首先,任何涉及NaN的操作(例如NaN/10)都会返回NaN,这个特点在多步计算中有可能导致问题。其次,NaN与任何值都不相等,包括NaN本身。

针对NaN的这两个特点,ECMAScript定义了isNaN()函数。这个函数接受一个参数,该参数可以是任何类型,而函数会帮我们确定这个参数是否“不是数值”。isNaN()在接收到一个值之后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串”10”或Boolean值。而任何不能被转换为数值的值都会导致这个函数返回true。

尽管有点儿不可思议,但isNaN()确实也适用于对象。在基于对象调用isNaN()函数时,会首先调用对象的valueOf()方法,然后确定该方法返回的值是否可以转换为数值。如果不能,则基于这个返回值再调用toString()方法,再测试返回值。而这个过程也是ECMAScript中内置函数和操作符的一般执行流程。

数值转换

有3个函数可以把非数值转换为数值:Number()、parseInt()和parseFloat()。第一个函数,即转型函数Number()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。这3个函数对于同样的输入会有返回不同的结果。

Number()函数转换规则如下:
1. 如果是Boolean值,true和false将分别被转换为1和0。
2. 如果是数字值,只是简单的传入和返回。
3. 如果是null值,返回0。
4. 如果是undefined,返回NaN。
5. 如果是字符串,遵循下列规则
- 如果字符串中只包含数字(包括前面带加号或头号的情况),则将其转换为十进制数值,即”1”会变成1,”123”会变成123,而”011”会变成11(注意:前导的零被忽略了)
- 如果字符串中包含有效的浮点格式,如”1.1”,则将其转换为对应的浮点数值(同样,也会忽略前导零)
- 如果字符串中包含有效的十六进制格式,例如”Oxf”,则将其转换为相同大小的十进制整数值
- 如果字符串是空的(不包含任何字符),则将其转换为0
- 如果字符串中包含除上述格式之外的字符,则将其转换为NaN。
6. 如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再次依照前面的规则转换返回的字符串值。

由于Number()函数在转换字符串时比较复杂而且不够合理,因此在处理整数的时候更常用的是parseInt()函数。parseInt()函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,直至找到第一个非空格字符。如果第一个字符不是数字字符或者负号,parseInt()就会返回NaN;也就是说,用parseInt()转换空字符串会返回NaN(Number()对空字符返回0)。如果第一个字符是数字字符,parseInt()会继续解析第二个字符,直到解析完所有后续字符或者遇到了一个非数字字符。例如,”1234blue”会被转换为1234,因为”blue”会被完全忽略。类似地,”22.5”会被转换为22,因为小数点并不是有效的数字字符。
如果字符串中的第一个字符是数字字符,parseInt()也能够识别出各种整数格式(即前面讨论的十进制、八进制和十六进制数)。也就是说,如果字符串以”Ox”开头且后跟数字字符,就会将其当作一个十六进制整数;如果字符串以”0”开头且后跟数字字符,则会将其当作一个八进制数来解析。

在ECMAScript 3 JavaScript引擎中,”070”被当成八进制字面量,因此转换后的值是十进制的56。而在ECMAScript 5 JavaScript引擎中,parseInt()已经不具有解析八进制值的能力,因此前导的零会被认为无效,从而将这个值当成”0”,结果就得到十进制的0。在ECMAScript5中,即使是在严格模式下也会如此。

为了消除在使用parseInt()函数时可能导致的上述困惑,可以为这个函数提供第二个参数:转换时使用的基数(即多少进制)。如果知道要解析的值是十六进制格式的字符串,那么指定基数16作为第二个参数,可以保证得到正确的结果,例如:

var num = parseInt"OxAF", 16); //175

实际上,如果指定了16作为第二个参数,字符串可以不带前面的”Ox”,如下所示:

var num1 = parseInt("AF", 16); //175
var num2 = parseInt("AF");     //NaN

这个例子中的第一个转换成功了,而第二个则失败了。差别在于第一个转换传入了基数,明确告诉parseInt()要解析一个十六进制格式的字符串;而第二个转换发现第一个字符不是数字字符,因此就自动终止了。

不指定基数意味着让parseInt()决定如何解析输入的字符串,因此为了避免错误的解析,我们建议无论在什么情况下都明确指定基数。

与parseInt()函数类似,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

String类型

String类型用于表示由零或多个16位Unicode字符组成的字符序列,即字符串。字符串可以由双引号(”)或单引号(’)表示。用双引号表示的字符串和用单引号表示的字符串完全相同。

字符字面量

String数据类型包含一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或者具有其他用途的字符。这些字符字面量如下表所示:

字面量含义
\n换行
\t制表
\b空格
\r回车
\f进纸
\斜杠
\’单引号(’),在用单引号表示的字符串中使用
\”双引号(”),在用双引号表示的字符串中使用
\xnn以十六进制代码nn表示的一个字符(其中n为0~F)
\unnnn以十六进制代码nnnn表示的一个Unicode字符(其中n为0~F)

任何字符串的长度都可以通过访问其length属性取得。

字符串的特点

ECMAScript中的字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量。

要把一个值转换为一个字符串有两种方式。第一种是使用几乎每个值都有的toString()方法。
数值、布尔值、对象和字符串值(没错,每个字符串也都有一个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(9));  //"10"
alert(num.toString(16)); //"a"

在不知道要转换的值是不是null或undefined的情况下,还可以使用转型函数String(),这个函数能够将任何类型的值转换为字符串。String()函数遵循下列转换规则:
1. 如果值有toString()方法,则调用该方法(没有参数)并返回相应的结果;
2. 如果值是null,则返回”null”;
3. 如果值是undefined,则返回”undefined”。

要把某个值转换为字符串,可以使用加号操作符(3.5节讨论)把它与一个字符串(”“)加在一起。

Object类型

CMAScript中的对象其实就是一组数据和功能的集合。对象可以通过执行new操作符后跟要创建的对象类型的名称来创建。而创建Object类型的实例并为其添加属性和(或)方法,就可以创建自定义对象,如下所示:

var o = new Object();

这个语法与Java中创建对象的语法相似;但在ECMAScript中,如果不给构造函数传递参数,则可以省略后面的那一对圆括号。也就是说,在像前面这个示例一样不传递参数的情况下,完全可以省略那对圆括号(但这不是推荐的做法):

var o = new Object//有效,但不推荐省略圆括号

仅仅创建Object的实例并没有什么用处,但关键是要理解一个重要的思想:即在ECMAScript中,(就像Java中的java.lang.Object对象一样)Object类型是所有它的实例的基础。换句话说,Object类型所具有的任何属性和方法也同样存在于更具体的对象中。

Object的每个实例都具有下列属性和方法:
- constructor:保存着用于创建当前对象的函数。
- hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。
- isPrototypeOf(object):用于检查传入的对象是否是另一个对象的原型。
- propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in语句(本章后面将会讨论)来枚举。
- toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
- toString():返回对象的字符串表示。
- valueOf():返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同。

操作符

  • 操作符包括算数操作符,位操作符,关系操作符和相等操作符。
  • 他们可以适用于很多值,包括字符串,数字值,布尔值,甚至对象。
  • 在应用于对象时,通常会调用对象的valueOf()和(或)toString()方法。

一元操作符

只能操作一个值的操作符叫做一元操作符。一元操作符是ECMAScript中最简单的操作符。

递增和递减操作符
  • 前置++ => ++a
  • 后置++ => a++
  • 前置– => –a
  • 后置– => a–

    效果同C语言
    不同的是,这四个操作符对任何值都适用。

  • 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减1的操作。字符串变量变成数值变量。

  • 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为NaN。字符串变量变成数值变量。
  • 在应用于布尔值false时,先将其转换为0再执行加减1的操作。布尔值变量变成数值变量。
  • 在应用于布尔值true时,先将其转换为1再执行加减1的操作。布尔值变量变成数值变量。
  • 在应用于浮点数值时,执行加减1的操作。
  • 在应用于对象时,先调用对象的valueOf()方法以取得一个可供操作的值。然后对该值应用前述规则。如果结果是NaN,则在调用toString()方法后再应用前述规则。对象变量变成数值变量。
var s1 = '2';
var s2 = 'z';
var s3 = '123z';
var b1 = false;
var b2 = true;
var f = 1.1;
var o = {
  valueOf: function() {
    return -1;
  }
};
s1++;    // s1 == 3
s2++;    // s2 == NaN
s3++;    // s3 == NaN
b1--;    // b1 == -1
b2--;    // b2 == 0
f--;     // f  == 0.100000000000009
o--;     // o == -2
一元加和减操作符
  • 一元加减操作符作用于数值,同C语言。
  • 一元加操作符作用于数值非数值,等同于Number()转型函数。
  • 一元减操作符作用于数值非数值,等同于一元加操作符最后将其转换成负数。

位操作符

  • 按位非(NOT)
    按位非操作符由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码。
  • 按位与(AND)
    按位与操作符由一个和号字符(&)表示,它有两个操作符数。
  • 按位或(OR)
    按位或操作符由一个竖线符号(|)表示,同样也有两个操作数。
  • 按位异或(XOR)
    按位异或操作符由一个插入符号(^)表示,也有两个操作数。
  • 左移
    左移操作符由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。右侧空出用0补充。(左移不影响符号位)
  • 有符号的右移
    有符号的右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位(即正负号标记)。
  • 无符号右移
    无符号右移操作符由3个大于号(>>>)表示,这个操作符会将数值的所有32位都向右移动。对正数来说,无符号右移的结果与有符号右移相同。对于负数来说则不同。
var a = -64;     // 二进制11111111111111111111111111000000
var b = a >>> 5; // 二进制00000111111111111111111111111111

布尔操作符

  • !

    • 如果操作数是一个对象,返回false;
    • 如果操作数是一个空字符串,返回true;
    • 如果操作数是一个非空字符串,返回false;
    • 如果操作数是数值0,返回true; 如果操作数是任意非0数值(包括Infinity),返回false;
    • 如果操作数是null,返回true;
    • 如果操作数是NaN,返回true; 如果操作数是undefined,返回true。

    !! 等同于 Boolean()

  • &&

    • 如果第一个操作数是对象,则返回第二个操作数;
    • 如果第二个操作数是对象,则只有在第一个操作数的求值结果为true的情况下才会返回该对象;
    • 如果两个操作数都是对象,则返回第二个操作数;
    • 如果有一个操作数是null,则返回null;
    • 如果有一个操作数是NaN,则返回NaN;
    • 如果有一个操作数是undefined,则返回undefined。

    不能在逻辑与操作中使用未定义的值

  • ||

    • 如果第一个操作数是对象,则返回第一个操作数;
    • 如果第一个操作数的求值结果为false,则返回第二个操作数;
    • 如果两个操作数都是对象,则返回第一个操作数;
    • 如果两个操作数都是null,则返回null;
    • 如果两个操作数都是NaN,则返回NaN;
    • 如果两个操作数都是undefined,则返回undefined。

    可以利用逻辑或的这一行为来避免为变量赋null或undefined值。
    var myObject = preferredObject || backupObject;

  • 乘性操作符

    • *
      • 如果操作数都是数值,执行常规的乘法计算,即两个正数或两个负数相乘的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了ECMAScript数值的表示范围,则返回Infinity或-Infinity;
      • 如果有一个操作数是NaN,则结果是NaN;
      • 如果是Infinity与0相乘,则结果是NaN;
      • 如果是Infinity与非0数值相乘,则结果是Infinity或-Infinity,取决于有符号操作数的符号;
      • 如果是Infinity与Infinity相乘,则结果是Infinity;
      • 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则。
    • /
      • 如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果商超过了ECMAScript数值的表示范围,则返回Infinity或-Infinity;
      • 如果有一个操作数是NaN,则结果是NaN;
      • 如果是Infinity被Infinity除,则结果是NaN;
      • 如果是零被零除,则结果是NaN;
      • 如果是非零的有限数被零除,则结果是Infinity或-Infinity,取决于有符号操作数的符号;
      • 如果是Infinity被任何非零数值除,则结果是Infinity或-Infinity,取决于有符号操作数的符号;
      • 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则。
    • %
      • 如果操作数都是数值,执行常规的除法计算,返回除得的余数;
      • 如果被除数是无穷大值而除数是有限大的数值,则结果是NaN;
      • 如果被除数是有限大的数值而除数是零,则结果是NaN;
      • 如果是Infinity被Infinity除,则结果是NaN;
      • 如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数;
      • 如果被除数是零,则结果是零;
      • 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则。

    加减操作符

    • +

      • 如果有一个操作数是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”。

  • -

    • 如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
    • 如果有一个操作数是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()方法并将得到的字符串转换为数值。
  • 关系操作符

    • 如果两个操作数都是数值,则执行数值比较。
    • 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
    • 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
    • 如果一个操作数是对象,则调用这个对象的valueOf()方法,用得到的结果按照前面的规则执行比较。如果对象没有valueOf()方法,则调用toString()方法,并用得到的结果根据前面的规则执行比较。
    • 如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。

    相等操作符

    • == / !=

      • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false转换为0,而true转换为1;
      • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
      • 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较;
      • null和undefined是相等的。
      • 要比较相等性之前,不能将null和undefined转换成其他任何值。
      • 如果有一个操作数是NaN,则相等操作符返回false,而不相等操作符返回true。
      • 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true;否则,返回false。
    • === / !===

      • 除了在比较之前不转换操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。

    条件操作符

    • variable = boolean_expression ? true_value : false_value;

    赋值操作符

    • =
    • +=
    • -=
    • *=
    • /=
    • %=
    • <<=
    • >>=
    • >>>=

    设计这些操作符的主要目的就是简化赋值操作。使用它们不会带来任何性能的提升。

    逗号操作符

    • 使用逗号操作符可以在一条语句中执行多个操作;
    • 逗号操作符还可以用于赋值。在用于赋值时,逗号操作符总会返回表达式中的最后一项。

    流控制语句

    if

    if(condition)statement1 else statement2
    其中的condition(条件)可以是任意表达式;而且对这个表达式求值的结果不一定是布尔值。ECMAScript会自动调用Boolean()转换函数将这个表达式的结果转换为一个布尔值。如果对condition求值的结果是true,则执行statementl(语句1),如果对condition求值的结果是false,则执行statement2(语句2)。

    do-while

    do-while语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。

    像do-while这种后测试循环语句最常用于循环体中的代码至少要被执行一次的情形。

    while

    while语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。

    for

    for语句也是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代码的能力。

    for-in

    for-in语句是一种精准的迭代语句,可以用来枚举对象的属性。

    ECMAScript对象的属性没有顺序。因此,通过for-in循环输出的属性名的顺序是不可预测的。具体来讲,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。

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

    label

    使用label语句可以在代码中添加标签,以便将来使用。

    标签可以在将来由break或continue语句引用。

    加标签的语句一般都要与for语句等循环语句配合使用。

    break和continue

    break语句会立即退出循环,强制继续执行循环后面的语句。而continue语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行。

    with

    with语句的作用是将代码的作用域设置到一个特定的对象中。

    定义with语句的目的主要是为了简化多次编写同一个对象的工作

    var qs = location.search.substring(1);
    var hostName = location.hostname;
    var url = location.href;
    
    // 等价于
    with(location) {
        var qs = search.substring(1);
        var hostName = hostname;
        var url = href;
    }

    严格模式下不允许使用with语句,否则将视为语法错误。

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

    switch

    作用同C语言,不同的是case语句可以是任意类型的常量,变量,表达式。

    函数

    • ECMAScript中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。
    • 在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每一个参数。
    • 命名的参数只提供便利,但不是必需的。
    • arguments对象可以与命名参数一起使用。
    • 修改arguments中的值会改变命名参数对应的值。
    • 修改命名参数不会改变arguments中对应的值。
    • 没有传递值的命名参数将自动被赋予undefined值。
    • 严格模式对如何使用argumetns对象做出了一些限制。
      • 修改arguments的值,将不会修改对应命名参数的值。
      • 重写arguments的值会导致语法错误(代码将不会执行)。
    • ECMAScript中的所有参数传递的都是值,不可能通过引用传递参数。
    • 没有重载,后定义函数会覆盖先定义函数。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值