持续更新...
主要是对《JavaScript高级程序设计》里的要点摘抄,构建自己的知识体系。
新手上路,如有不对或不足的地方,还请大家多多指正 :)
第2章 ECMAScript基础
ECMAScript仅仅是一个描述,定义了脚本语言的所有属性、方法和对象。其他语言可以实现ECMAScript来作为功能的基准,如JavaScript。
2.1 语法
ECMAScript的基础概念如下:
- 区分大小写;与Java一样,变量,函数名,运算符以及其他一切东西都是区分大小写的。
- 变量是弱类型的; 与Java和C不同,ECMAScript中的变量无特定的类型。定义变量时只用var运算符,可以将它初始化为任意的值。
- 每行结尾的分号可有可无;(最好的代码编写习惯是总加入分号:)
- 注释与Java、C和PHP语言的注释相同;
- 括号表明代码块;
2.2 变量
如前所述,ECMAScript中的变量使用var运算符(variable的缩写) 加变量名定义的。
用同一个var语句定义的变量不必具有相同的类型,如
var test = "hi", age = 25;
此外,变量还可以存放不同类型的值,这也是弱类型变量的优势。
变量名需遵守第一个字符必须是字母,下划线或$符号。
变量还应遵守一下某条著名的命名规则:
- Camel标记法——首字母小写,接下来字母以大写开头。
var myTestValue = 0;
- Pascal标记法——首字母大写,接下来字母以大写开头。
var MyTestValue = 0;
- 匈牙利类型标记法——在以Pascal标记法命名的变量前附加一个小写字母,以说明该变量的类型。
var iMyTestValue = 0; var sMySecondValue = "hi";
ECMAScript另一个有趣的方面是在使用变量之前不必声明,例如
var sTest = "hello";
sTest2 = sTest + " world";
alert(sTest2); //outputs "hello world"
就像已经声明过它。
ECMAScript的解释程序遇到未声明的标识符时,用该变量名创建一个全局变量,并将其初始化为指定的值。不过如果不能紧密跟踪变量,这样做也很危险。最好的习惯是像使用其他程序设计语言一样,总是声明所有变量。
2.3 关键字
2.4 保留字
2.5 原始值和引用值
在ECMAScript中,变量可以存放两种类型的值,即原始值和引用值。
- 原始值是存储在栈中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
- 引用值是存储在堆中的对象,也就是说,存储在变量处的是一个指针,指向存储对象的内存处。
为变量赋值时,ECMAScript的解释程序必须判断该值是原始类型的,还是引用类型的。要实现这一点,解释程序则需尝试判断该值是否为ECMAScript的原始类型之一,即Undefined , Number , Null , Boolean 和String型。由于这些原始类型占据的空间是固定的,所以可将它们存储在较小的内存区域——栈中,这样存储便于迅速查寻变量的值。
而如果一个值是引用类型的,那么它的存储空间将从堆中分配。由于引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量的性能无任何负面影响。
2.6 原始类型
- Undefined类型 ;当声明的变量未初始化时,该变量的默认值是undefined。
- Null类型;值undefined实际上是从值null派生来的,null用于表示尚未存在的对象。
- Boolean类型;true or false。
- Number类型;Number类型既可以表示32位的整数,还可以表示64位的浮点数。 浮点字面量的有趣之处在于用它进行计算前,真正存储的是字符串;特殊值NaN
- String类型;String类型的独特之处在于,它是唯一没有固定大小的原始类型。可由单引号或双引号声明。
typeof运算符:ECMAScript把术语类型(type)定义为值的一个集合,并提供了typeof运算符来判断一个值是否在某种类型的范围内。
2.7 转换
所有程序设计语言最重要的特征之一是具有进行类型转换的能力,ECMAScript给开发者们提供了大量简单的转换方法,都是简短的一步操作。
- 转换成字符串;ECMAScript的Boolean值,数字和字符串的原始值的有趣之处在于它们都是伪对象,这意味着它们实际上具有属性和方法。这3种主要的原始值都有toString( )方法,可以把它们的值转换成字符串。
Boolean类型的toString()方法只是输出“true”或“false”,结果由变量的值决定。
Number类型的toString()方法比较特殊,它有默认模式和基模式。默认模式只是用相应的字符串输出数字值的十进制形式;而基模式可以用不同的基输出数字,例如二进制的基是2,八进制的基是8,十六进制的基是16。例如
var num = 10;
alert(num.toString(2)); //outputs "1010"
alert(num.toString(8)); //outputs "12"
alert(num.toString(16)); //outputs "A"
- 转换成数字; ECMAScript提供了parseInt()和parseFloat()两种把非数字的原始值转换成数字的方法。but,只有对String类型调用这些方法,它们才能正确运行;对其它类型返回的都是NaN。
此外,parseInt()方法还有基模式,可以把二进制,八进制,十六进制或其它任何进制的字符串转换成整数,基是由方法的第二个参数指定的。
var num1 = parseInt("AF",16); //return 175
var num2 = parseInt("10",8); //return 8
- 强制类型转换; ECMAScript中可用的3种强制类型转换如下:
- Boolean(value)——把给定的值转换成Boolean型
- Number(value)——把给定的值转换成数字(可以使整数或浮点数)
- String(value)——把给定的值转换成字符串
在处理ECMAScript这样的弱类型语言时,强制类型转换非常有用,不过应该确保使用值的正确。
2.8 引用类型
引用类型通常叫做类(class),也就是说,遇到引用值时,所处理的就是对象。
对象是由new运算符加上要实例化的类的名字创建的。例如,下面代码创建了Object类的实例:
var o = new Object();
第3章将更深入地探讨对象及其行为。这一节的重点是具有等价的原始类型的引用类型。
- Object类;ECMAScript中的所有类都由这个类继承而来,Object类中的所有属性和方法都会出现在其他类中,所以理解了Object类,就可以更好地理解其他类。
Object类具有下列属性:
- Constructor——对创建对象的函数的引用(指针)。
- Prototype——对该对象的对象原型的引用。对于所有类,它默认返回Object对象的一个实例。
Object类还有几个方法:
- HasOwnProperty(property)——判断对象是否有某个特定的属性,必须用字符串指定该属性。例如
o.hasOwnProperty("name");
- IsPrototypeOf(object)——判断该对象是否为另一个对象的原型。
- PropertyIsEnumerable(property)——判断给定的属性是否可以用for...in语句进行枚举。
- ToString()——返回对象的原始字符串表示。
- ValueOf()——返回最适合该对象的原始值。 对于许多类,该方法返回的值都与toString()的返回值相同。
上面列举的每种属性和方法都会被其他类覆盖。
- Boolean类
Boolean类是Boolean原始类型的引用类型。需传递Boolean值作为参数:
var booleanObject = new Boolean(true);
遗憾的是,在ECMAScript中很少使用Boolean对象,即使使用,也不易理解。问题通常出现在Boolean表达式中使用Boolean对象时。例如:
var falseObject = new Boolean(false);
var result = falseObject && true; //outputs true
按正常的理解,false 和 true进行AND操作的结果是false。不过,在这行代码中,计算的是falseObject,而不是它的值false。因为在Boolean表达式中,所有对象都会被自动转换为true。so,以上的进行的AND操作结果为true。
- Number类
要创建Number对象,采用如下代码:
var numberObject = new Number(55);
要得到数字对象的Number原始值,只需要使用valueOf()方法:
var number = numberObject.valueOf();
当然,Number类也有toString()方法,在讨论类型转换的小节中已经讨论过该方法。
除从Object类继承的标准方法外,Number还有几个处理数值的专用方法。
toFixed()返回的是具有指定位数小数的数字的字符串表示。例如:
alert(numberObject.toFixed(2)); outputs "55.00"
对于处理货币的应用程序,该方法非常有用。不过toFixed()方法只能表示具有0到20位小数的数字,超出这个范围的值会引发错误。
- String类
String对象的valueOf()方法和toString()方法都会返回String型的原始值。
String类具有属性length,它是字符串中的字符个数。ps. 另外即使字符串中包含双字节的字符,每个字符也只算一个字符。
String类具有大量的方法。首先,两个方法charAt()和charCodeAt()访问的是字符串中的单个字符。大家应该非常熟悉,第一个字符的位置是0,第二个字符的位置是1,以此类推。这两个方法都有一个参数,即要操作的字符的位置。
charAt()方法返回的是指定位置处的字符:
var stringObject = new String("hello world");
alert(stringObject.charAt(1)); outputs "e"
如果想得到的不是字符,而是字符代码,那么可以调用charCodeAt();
接下来是concat()方法,用于把一个或多个字符串连接到String对象的原始值上。该方法返回的是String原始值,保持原始的String对象不变:
var stringObject = new String("hello ");
var result = stringObject.concat("world");
alert(result); //outputs "hello world"
alert(stringObjejct); //outputs "hello "
实际中较常见的是用加号(+)连接字符串;
如果无法确定在某个字符串中是否存在一个字符,这时可调用indexOf()和lastIndexOf()方法。这两个方法的不同之处在于,indexOf()方法是从字符串的开头(位置0)开始检索,而lastIndexOf()是从结尾开始。
下一个方法是localeCompare(),对字符串值进行排序。该方法有一个参数——要进行比较的字符串。(大写字母在顺序上排在小写字母之后)
instanceof运算符
在使用typeof运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都会返回“object”。so,ECMAScript引入了另一个Java运算符来解决这个问题。
与typeof方法不同的是,instanceof方法要求开发者明确地确认对象为某特定类型。 例如:
var stringObject = new String("hi");
alert(stringObject instanceof String); //outputs "true"
2.9 运算符
- 一元运算符; 一元运算符只有一个参数,即要操作的对象或值。它们是ECMAScript中最简单的运算符。
- delete
- void
- 前增量 / 前减量运算符 (++i/--i)
- 后增量 / 后减量运算符 (i++/i--)
- 一元加法和一元减法
- 位运算符; 这套运算符是在数字底层(即表示数字的32个数位)进行操作的。在探讨这些运算符之前,你首先需要重温一下ECMAScript中的整数!
位运算NOT(~); 它是ECMAScript中为数不多的与二进制算术有关的运算符之一。位运算NOT是三步的处理过程:
(1) 把运算数转换成32位数字;
(2) 把二进制形式转换成它的二进制反码;
(3) 把二进制反码转换成浮点数;
例如:
var num1 = 25; //25 is equal to 0000000000000000011001
var num2 = ~num1 //convert to 1111111111111111100110
alert(num2); // outputs "-26"
位运算符NOT实质上是对数字求负,然后减1,因此 25 变为 -26。
位运算AND(&); 位运算符AND由和号(&)表示,直接对数字的二进制形式进行运算。它把每个数字中的数位对齐,然后用下面的规则对同一位置上的两个数位进行AND运算:
第一个数字中的数位 | 第二个数字中的数位 | 结果 |
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
例如,要对数字 25 和 3 进行AND运算,代码如下所示:
var result = 25 & 3;
alert(result); // outputs "1"
其结果是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 中,只有一个位数(位0)存放的是1,其他位数生成的都是0,所以结果是1。
位运算OR(|); 在计算每个位时,OR采用下列规则:
第一个数字中的数位 | 第二个数字中的数位 | 结果 |
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
对 25 和 3 进行OR运算,结果为 28。
位运算XOR(^); XOR不同于OR,当只有一个数位存放的是1时,它才返回1。
对 25 和 3 进行XOR运算,结果为 26。
左移运算(<<); 左移运算符是把数字中的所有数位向左移动指定的数量。
例如,把数字2(等于二进制中的10)左移5位,结果为64(等于二进制中的100000)。
注意在左移数位时,数字右边会多出5个空位。左移运算用 0 填充这些空位,使结果为完整的32位数字。
有符号右移运算(>>); 它将32位数字中的所有数位整体右移,同时保留该数的符号。同样,移动数位后会造成空位。这次,空位位于数字的左侧,但位于符号位之后。ECMAScript用符号位的值填充这些空位,创建完整的数字。
有意思的是,有符号右移运算与左移运算相反。例如,把64右移5位,将变为2.
无符号右移运算(>>>); 对于正数,无符号右移运算的结果与有符号右移运算的结果一样。
但对于负数,情况就不同了。无符号右移运算用0填充所有空位,而负数则被作为正数来处理,所以负数的无符号右移运算得到的总是一个非常大的数字。出于这种原因,使用无符号右移运算要小心。
- Boolean运算符; Boolean运算符与等式运算符同等重要,是它们使得程序设计语言得以正常运行。Boolean运算符有三种,即NOT(!)、AND(&&)和OR(||)。
- 乘性运算符; 乘性运算符有三种,乘法运算符(*)、除法运算符(/)和取模(余数)运算符(%)。不过它们还具有一些自动的类型转换功能,这一点需要注意。
- 加性运算符; 在程序设计语言中,加性运算符(即加号和减号)通常是最简单的数学运算符。不过在ECMAScript中,每个加性运算符都有大量的特殊行为。
加法运算符(+),如果两个运算数都是数字,将执行算术加法。不过如果某个运算符是字符串,那么采用下列规则:
1、如果两个运算数都是字符串,把第二个字符串连接到第一个字符串上。
2、如果只有一个运算数是字符串,把另一个运算数转换成字符串,结果是两个字符串连接成的字符串。例如:
var result = 5 + 5; // two numbers
alert(result); //outputs "10"
var result2 = 5 + "5"; //a number and a string
alert(result2); //outputs "55"
- 关系运算符; 关系运算符 小于(<)、大于(>)、小于等于(<=)和大于等于(>=)执行的是两个数的比较运算,比较方式与算术比较运算相同。每个关系运算符都返回一个Boolean值。
- 等性运算符; 判断两个变量是否相等是程序设计中非常重要的运算。在处理原始值时,这种运算相当简单,但涉及对象,任务就稍有点复杂。ECMAScript提供了两套运算符处理这个问题,等号和非等号用于处理原始值,全等号和非全等号用于处理对象。
- 条件运算符; 条件运算符是ECMAScript中功能最多的运算符,它的形式与Java中的相同:variable = boolean_expression ? true_value : false_value; 例如:
var max = (num1 > num2) ? num1 : num2;
在这个例子中,max将被赋予数字中的最大值。
- 赋值运算符; 简单的赋值运算符由等号(=)实现,只是把等号右边的值赋予等号左边的变量。而复合赋值运算是一些缩写形式。比如:乘法/赋值(+=)、除法/赋值(/=)、取模/赋值(%=)……等等.
2.10 语句
- if 语句;
- 迭代语句 (循环语句); ECMAScript提供了四种迭代语句:do-while语句,while语句,for语句和for-in语句(for-in语句用于枚举对象的属性)。
- 有标签的语句; 可以用下列语法给语句加标签,以便日后调用:
label: statement
例如:
start: var count = 10;
标签start可被后来的break语句或continue语句引用。
- break 语句 和 continue 语句;
- with 语句;
- switch 语句;ECMAScript和Java中的switch语句有两点不同。在ECMAScript中,switch语句可以用于字符串,而且能用不是常量的值说明情况:
var BLUE ="blue", RED = "red", YELLOW = "yellow"; switch(color){ case BLUE: alert("Blue"); break; case RED: alert("Red"): break; case YELLOW: alert("Yellow"); break; default: alert("Other"); }
这里,switch语句用于字符串color,声明case使用的是变量BLUE、RED和YELLOW,这在ECMAScript中是完全有效的。
2.11 函数
- 无重载; ECMAScript中的函数不能重载。可用相同的名字在同一个作用域中定义两个函数而不会引发错误,但真正使用的是后一个函数。考虑下面的例子:
function doAdd(num){ alert(num + 100); } function doAdd(num){ alert(num + 10); } doAdd(10);
你认为这段代码会显示什么?
警告将显示“20”,因为第二个doAdd()函数定义覆盖了第一个定义。虽然这让开发者有些头疼,不过可以使用arguments对象避开这种限制。
- arguments 对象; 在函数代码中,使用特殊对象arguments,开发者无需明确指出参数名就能访问它们。
用arguments对象判断传递给函数的参数个数,即可模拟函数重载:
function doAdd(){
if(arguments.length == 1){
alert(arguments[0] + 10);
} else if(arguments.length == 2){
alert(arguments[0] + arguments[1]);
}
}
doAdd(10); //outputs "20"
doAdd(30,20); //outputs "50"
虽然不如重载那么好,不过已足可避开ECMAScript的这种限制。
- Funtion 类; 尽管可用Function构造函数创建函数,但最好不要使用它,因为用它定义函数比用传统方式慢得多。不过,所有函数都应看做是Function类的实例。还有两个Function类的方法与对象的谈论相关,下一章将讨论它们。
- 闭包; ECMAScript最容易让人误解的一点是它支持闭包(closure)。所谓闭包,就是指该函数能使用函数外定义的变量。在ECMAScript中使用全局变量是一个简单的闭包实例:
var message = "hi"; function sayHi(){ alert(message); } sayHi();
而在一个函数中定义另一个函数会使闭包变得更复杂,如下所示:
var baseNum = 10; function addNumbers(num1,num2){ function doAddition(){ return num1 + num2 + baseNum; } return doAddition(); }
这里要掌握的重要概念是doAddition()函数根本不接受参数,它使用的值是从执行环境中获取的
可以看到,闭包是ECMAScript中非常强大多用的一部分。就像使用任何高级函数一样,在使用闭包时要当心,因为它们可能会变得非常复杂。