转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/7234053.html
一:基础
1:语法
ECMAScript 中的变量无特定的类型,定义变量时只用 var 运算符,可以将它初始化为任意值。
ECMAScript 允许开发者自行决定是否以分号结束一行代码。最好的代码编写习惯是总加入分号,因为没有分号,有些浏览器就不能正确运行。
ECMAScript 有两种类型的注释:
- 单行注释以双斜杠开头(//)
- 多行注释以单斜杠和星号开头(/*),以星号和单斜杠结尾(*/)
2:变量
变量声明不是必须的:ECMAScript 的解释程序遇到未声明过的标识符时,用该变量名创建一个全局变量,并将其初始化为指定的值。(不过不推荐这样做,这会让后来者摸不清头脑!)
命名规则:驼峰命名法、pascal命名法(单词开头大写)、匈牙利类型标记法。
匈牙利类型标记法:以 Pascal 标记法命名的变量前附加一个小写字母(或小写字母序列),说明该变量的类型。
类型 | 前缀 | 示例 |
---|---|---|
数组 | a | aValues |
布尔型 | b | bFound |
浮点型(数字) | f | fValue |
函数 | fn | fnMethod |
整型(数字) | i | iValue |
对象 | o | oType |
正则表达式 | re | rePattern |
字符串 | s | sValue |
变型(可以是任何类型) | v | vValue |
3:保留字
abstract | arguments | boolean | break | byte |
case | catch | char | class* | const |
continue | debugger | default | delete | do |
double | else | enum* | eval | export* |
extends* | false | final | finally | float |
for | function | goto | if | implements |
import* | in | instanceof | int | interface |
let | long | native | new | null |
package | private | protected | public | return |
short | static | super* | switch | synchronized |
this | throw | throws | transient | true |
try | typeof | var | void | volatile |
while | with | yield |
4:变量类型
在 ECMAScript 中,变量可以存在两种类型的值,即原始值和引用值。
- 原始值
- 存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
- 引用值
- 存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。
可以用typeof 运算符来判断一个值是否在某种类型的范围内:
alert (typeof sTemp); //输出 "string"
alert (typeof 86); //输出 "number"
在使用 typeof 运算符判断引用类型存储值时会出现一个问题:无论引用的是什么类型的对象,它都返回 "object"。
因此,ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题:
alert(“a string” instanceof String); //输出 "true"
instanceof 方法要求开发者明确地确认对象为某特定类型,上面代码问的是“"a string" 是否为 String 对象的实例?”。
ECMAScript 有 5 种原始类型(primitive type),即 Undefined、Null、Boolean、Number 和 String:
Undefined 类型
Undefined 类型只有一个值,即 undefined。当声明的变量未初始化时,该变量的默认值是 undefined。
var oTemp;
Null 类型
它只有一个专用值 null,即它的字面量。值 undefined 实际上是从值 null 派生来的,因此 ECMAScript 把它们定义为相等的。
特殊的 Number 值
Number.MAX_VALUE 和 Number.MIN_VALUE:它们定义了 Number 值集合的外边界。所有 ECMAScript 数都必须在这两个值之间。
表示无穷大:Number.POSITIVE_INFINITY 的值为正无穷。Number.NEGATIVE_INFINITY 的值为负无穷。可以对任何数调用 isFinite() 方法,以确保该数不是无穷大。
NaN:表示非数(Not a Number),奇特之处在于,它与自身不相等!因此,判断非数时不要简单用==NaN或 !=NaN 进行比较,而是使用 isNaN(内容) 。
引用类型
引用类型通常叫做类(class),也就是说,遇到引用值,所处理的就是对象。
对象是由 new 运算符加上要实例化的对象的名字创建的,类似Java。
所有对象的基类—— Object,其中最重要的两个自带函数:
ToString():返回对象的原始字符串表示。
ValueOf():返回最适合该对象的原始值。对于许多对象,该方法返回的值都与 ToString() 的返回值相同。
最常用的两种内置引用类型是——Number和String
Number 对象
创建:
var oNumberObject = new Number(68);
要得到数字对象的 Number 原始值,只需要使用 valueOf() 方法。
Number 对象还有几个处理数值的专用方法:
toFixed() 方法:返回具有指定位数小数的数字的字符串表示。
alert(oNumberObject.toFixed(2)); //输出 "68.00"
toExponential():返回用科学计数法表示的数字的字符串形式,有一个参数,指定要输出的小数的位数。
alert(oNumberObject.toExponential(1)); //输出 "6.8e+1"
toPrecision():根据舍入位数对Number对象进行四舍五入。
alert(oNumberObject.toPrecision(1)); //输出 "7e+1" //68个位四舍五入得到70
String 对象
String 对象的 valueOf() 方法和 toString() 方法都会返回 String 类型的原始值:
alert(oStringObject.valueOf() == oStringObject.toString()); //输出 "true"
属性 length:它是字符串中的字符个数。
charAt() 和 charCodeAt(): 访问的是字符串中的单个字符,这两个方法都有一个参数,即要操作的字符的位置。
charAt() 方法返回的是包含指定位置处的字符;charCodeAt() 方法返回的不是字符,而是字符的ASCII码。
concat() 方法:用于把一个或多个字符串连接到 String 对象的原始值上。该方法返回的是 String 原始值,保持String 对象不变。
var oStringObject = new String("hello ");
var sResult = oStringObject.concat("world");
alert(sResult); //输出 "hello world",返回的是一个原始值结果
alert(oStringObject); //输出 "hello ",对象不变
indexOf() 和 lastIndexOf() 方法返回的都是指定的子串在另一个字符串中的位置,如果没有找不到子串,则返回 -1。
indexOf() 方法是从字符串的开头(位置 0)开始检索字符串,而 lastIndexOf() 方法则是从字符串的结尾开始检索子串。
localeCompare():对字符串进行排序。该方法有一个参数 - 要进行比较的字符串,返回的是下列三个值之一:
- 如果 String 对象按照字母顺序排在参数字符串之前,返回负数。
- 如果 String 对象等于参数字符串,返回 0
- 如果 String 对象按照字母顺序排在参数字符串之后,返回正数。
slice() 和 substring():从子串创建字符串值,即 slice() 和 substring()。
方法第一个参数是要获取的子串的起始位置,第二个参数(如果使用的话)是要获取子串终止前的位置(也就是说,获取终止位置处的字符不包括在返回的值内)。如果省略第二个参数,终止位就默认为字符串的长度。
对于负数参数,slice() 方法会用字符串的长度加上参数(即倒序切片),substring() 方法则将其作为 0 处理(也就是说将忽略它)。
slice() 和 substring() 方法都不改变 String 对象自身的值。它们只返回原始String 值,保持 String 对象不变。
大小写转换:
有 4 种方法用于执行大小写转换,即
- toLowerCase()
- toLocaleLowerCase()
- toUpperCase()
- toLocaleUpperCase()
二:运算
1:位运算
~:not运算
&:与运算
|:或运算
^:异或运算
<<:左移运算
>>:右移运算
>>>:无符号右移:将无符号 32 位数的所有数位整体右移
2:逻辑运算
ToBoolean操作:把其它类型转换为布尔类型
参数类型 | 结果 |
---|---|
Undefined | false |
Null | false |
Boolean | 结果等于输入的参数(不转换) |
Number | 如果参数为 +0, -0 或 NaN,则结果为 false;否则为 true。 |
String | 如果参数为空字符串,则结果为 false;否则为 true。 |
Object | true |
Boolean 运算符有三种:NOT、AND 和 OR。
3:算术运算
乘法运算符:由星号(*)表示,用于两数相乘。
乘法还有一些特殊行为:
- 如果结果太大或太小,那么生成的结果是 Infinity 或 -Infinity。
- 如果某个运算数是 NaN,结果为 NaN。
- Infinity 乘以 0,结果为 NaN。
- Infinity 乘以 0 以外的任何数字,结果为 Infinity 或 -Infinity。
- Infinity 乘以 Infinity,结果为 Infinity。
除法运算符:由斜杠(/)表示,用第二个运算数除第一个运算数。
除法运算符也有一些特殊行为:
- 如果结果太大或太小,那么生成的结果是 Infinity 或 -Infinity。
- 如果某个运算数是 NaN,结果为 NaN。
- Infinity 被 Infinity 除,结果为 NaN。
- Infinity 被任何数字除,结果为 Infinity。
- 0 除一个任何非无穷大的数字,结果为 NaN。
- Infinity 被 0 以外的任何数字除,结果为 Infinity 或 -Infinity。
除法(余数)运算符:由百分号(%)表示。
取模运算符也有特殊的行为:
- 如果被除数是 Infinity,或除数是 0,结果为 NaN。
- Infinity 被 Infinity 除,结果为 NaN。
- 如果除数是无穷大的数,结果为被除数。
- 如果被除数为 0,结果为 0。
加法也有一些特殊行为:
- 某个运算数是 NaN,那么结果为 NaN。
- -Infinity 加 -Infinity,结果为 -Infinity。
- Infinity 加 -Infinity,结果为 NaN。
- +0 加 +0,结果为 +0。
- -0 加 +0,结果为 +0。
- -0 加 -0,结果为 -0。
减法运算符也有一些特殊行为:
- 某个运算数是 NaN,那么结果为 NaN。
- Infinity 减 Infinity,结果为 NaN。
- -Infinity 减 -Infinity,结果为 NaN。
- Infinity 减 -Infinity,结果为 Infinity。
- -Infinity 减 Infinity,结果为 -Infinity。
- +0 减 +0,结果为 +0。
- -0 减 -0,结果为 -0。
- +0 减 -0,结果为 +0。
- 某个运算符不是数字,那么结果为 NaN。
4:关系运算
这里记录几种特殊的比较:
对两个字符串应用关系运算符:对于字符串,第一个字符串中每个字符的代码都与会第二个字符串中对应位置的字符的代码进行数值比较,而不是根据字典序比较。因此,要强制性得到按照真正的字母顺序比较的结果,必须把两个数转换成相同的大小写形式(全大写或全小写的),然后再进行比较:
var bResult = "Blue".toLowerCase() < "alpha".toLowerCase();
比较数字与字符串:比较一个数字和一个字符串,ECMAScript 都会把字符串转换成数字,然后按照数字值比较它们。如果字符串不是数字字符串,调用 parseInt() 方法时返回的是 NaN。根据规则,任何包含 NaN 的关系运算都会返回 false。
两套等性运算符:等号和非等号用于处理原始值,全等号和非全等号用于处理对象。
等号由双等号(==)表示,非等号由感叹号加等号(!=)表示。为确定两个运算数是否相等,这两个运算符都会对比较数进行类型转换:
执行类型转换的规则如下:
- 如果一个运算数是 Boolean 值,在检查相等性之前,把它转换成数字值。false 转换成 0,true 为 1。
- 如果一个运算数是字符串,另一个是数字,在检查相等性之前,要尝试把字符串转换成数字。
- 如果一个运算数是对象,另一个是字符串,在检查相等性之前,要尝试把对象转换成字符串。
- 如果一个运算数是对象,另一个是数字,在检查相等性之前,要尝试把对象转换成数字。
全等号由三个等号表示(===),非全等号由感叹号加两个等号(!==)表示。这两个运算符,不会对两边操作数执行类型转换。
var sNum = "66";
var iNum = 66;
alert(sNum == iNum); //输出 "true" //先转换为方便比较的类型
alert(sNum === iNum); //输出 "false" //不转换类型
5:条件运算: 条件?动作1 : 动作2
6:赋值运算
简单的赋值:=
复合赋值运算符:
- 乘法赋值(*=)
- 除法赋值(/=)
- 取模赋值(%=)
- 加法赋值(+=)
- 减法赋值(-=)
- 左移赋值(<<=)
- 有符号右移赋值(>>=)
- 无符号右移赋值(>>>=)
7:逗号运算符:用逗号运算符可以在一条语句中执行多个操作,常用语声明多个变量。
三:语句
1:条件语句
if
(condition1) statement1
else if
(condition2) statement2
else
statement3
2:循环
do
{statement}
while(expression);
while(expression){
statement
}
for(initialization; expression; post-loop-expression) {
statement
}
for(var in vars){
statement
}
3:标签
label : statement
标签语句可以被之后的 break 或 continue 语句引用。
4:break 和 continue语句
break 语句可以立即退出循环,阻止再次反复执行任何代码。
continue 语句只是退出当前循环,根据控制表达式还允许继续进行下一次循环。
break 语句和 continue 语句都可以与有标签的语句联合使用,返回代码中的特定位置:
break label
continue label
5:switch语句
switch (expression)
case value: statement;
break;
case value: statement;
break;
case value: statement;
break;
case value: statement;
break;
...
case value: statement;
break;
default: statement;
在 ECMAScript 中,switch 语句可以用于字符串,而且能用不是常量的值作为case:
var BLUE = "blue", RED = "red", GREEN = "green";
switch (sColor) {
case BLUE: alert("Blue");
break;
case RED: alert("Red");
break;
case GREEN: alert("Green");
break;
default: alert("Other");
}
四:函数
1:声明
function functionName(arg0, arg1, ... argN) {
statements
}
2:统一接收参数——特殊对象 arguments
可以使用arguments[i]接收调用函数时传过来的第i个参数。
通过属性 arguments.length 可以知道调用函数时传进来多少个参数。进一步,可以通过arguments.length根据传递给函数的参数个数不同,执行不同的代码,实行轻量级的函数重载模拟:
function doAdd() {
if(arguments.length == 1) {
alert(arguments[0] + 5);
} else if(arguments.length == 2) {
alert(arguments[0] + arguments[1]);
}
}
3:Function 类——函数也是对象,一个函数是一个Function类实例
可以像创建实例对象一样去定义一个具体函数:
var function_name = new Function(arg1, arg2, ..., argN, function_body)
属性 length 声明了函数期望的参数个数:
alert(函数名.length);
Function 对象的valueOf() 方法和 toString() 方法:这两个方法返回的都是函数的源代码,在调试时尤其有用。
4:闭包——函数可以使用函数之外定义的变量。
最简单的闭包:方法使用类中定义的全局变量。
常见的闭包:外部函数接收参数,内部函数使用外部函数的变量进行计算并返回结果,外部函数则把内部函数作为返回值。
function addNum(iNum1, iNum2) {
function doAdd() {
return iNum1 + iNum2 + iBaseNum;
}
return doAdd();
}
五:对象
1:声明并实例化对象
var oObject = new Object();
2:无用对象回收
当再没有对对象的引用时,称该对象被废除(dereference)了。
运行无用存储单元收集程序时,所有废除的对象都被销毁。每当函数执行完它的代码,无用存储单元收集程序都会运行,释放所有的局部变量,还有在一些其他不可预知的情况下,无用存储单元收集程序也会运行。
对象的所有引用都设置为 null,可以强制性地废除对象:
var oObject = new Object;
oObject = null;//废除对象,这意味着下次运行无用存储单元收集程序时,该对象将被销毁。
3:对象类型
一般来说,可以创建并使用的对象有三种:本地对象、内置对象和宿主对象。【这句话很重要:也就是说,ES不存在使用class关键字定义一个类这种说法。但是我们可以创建一个最普通的Object类对象,在其上添加属性、方法,以获得我们想要的对象。这种方法后面会详解,叫工厂方法。】
本地对象就是 ECMA-262 定义的类(引用类型)。它们包括:
- Object
- Function
- Array
- String
- Boolean
- Number
- Date
- RegExp
- Error
- EvalError
- RangeError
- ReferenceError
- SyntaxError
- TypeError
- URIError
内置对象:在 ECMAScript 程序开始执行时出现,这意味着开发者不必明确实例化内置对象,它已被实例化了。
ECMA-262 只定义了两个内置对象,即 Global 和 Math,比如:我们可以直接使用Math.XXX进行属性与方法调用。
宿主对象:由 ECMAScript 实现的宿主环境提供的对象。
所有 BOM 和 DOM 对象都是宿主对象。
4:作用域
ECMAScript 中只存在一种作用域 - 公用作用域。ECMAScript 中的所有对象的所有属性和方法都是公用的
ECMAScript 也没有静态作用域,不过,它可以在构造函数中再次定义属性和方法,达到类似于静态内容的效果。
构造函数只是一个函数,而函数是对象,对象可以有属性和方法!
function sayHello() {
alert("hello");
}
sayHello.alternate = function() { //为sayHello方法添加一个方法属性
alert("hi");
}
sayHello(); //输出 "hello"
sayHello.alternate(); //输出 "hi" //调用函数对象内的函数
5:this
它用在对象的方法中。关键字 this 总是指向调用该方法的对象。
6:定义类或对象
1)工厂方式(常用)
定义一个工厂函数,创建并返回拥有特定属性与方法的对象。
function className(形参列表) {
var oTemp = new Object; //定义一个对象
oTemp.属性1 = 赋值; //为对象添加一系列属性
oTemp.属性2=赋值;
oTemp.属性3 = 赋值;
oTemp.函数 = function(参数列表) { //为对象添加函数
函数体
};
return oTemp; //返回这个对象
}
2)构造函数方式
ES中,函数也是对象,函数内可以进一步定义属性/方法。
因此,我们可以通过定义函数的方式来构造出一个对象:
function ClassName(参数列表) {
this.属性1 =参数1;
this.属性2 =参数2;
this.属性3 =参数3;
this.函数名 = function(参数列表) {
函数体
};
}
这样,虽然以function定义,但其实就是一个对象。
3)原型方式
利用了对象的 prototype 属性,通过它来为类添加属性、方法。
function ClassName() {//首先定义一个空函数对象,赋予类名
}
//然后,通过该类的prototype属性为该类添加属性/方法
ClassName.prototype.属性1 = 赋值;
ClassName.prototype.方法1 = function(参数列表) {
方法体
};
4)构造+原型的混合方式(常用)
用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数方法属性。结果是,所有函数都只创建一次,而每个对象都具有自己的对象属性实例。
function ClassName(参数列表) {//构造函数为对象的非函数属性赋值
this.属性1 = 参数1;
this.属性2 = 参数2;
this.属性3 = 参数3;
......
}
ClassName.prototype.函数名 = function(参数列表) { //使用原型方式添加函数属性
方法体
};
7:修改对象
prototype 可以为本地对象添加属性和方法。
1)创建新方法
本地对象.prototype.newfun = function() {
//todo
};
2)重写已有方法
本地对象.prototype.原有方法 = function() {
//新方法体
}
六:继承
所有开发者定义的类都可作为基类。
出于安全原因,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击。
创建的子类将继承超类的所有属性和方法,包括构造函数及方法的实现。因为所有属性和方法都是公用的,因此子类可直接访问这些方法。
子类还可添加超类中没有的新属性和方法,也可以覆盖超类的属性和方法。
ES5中继承的实现:
1)对象冒充方式
其原理如下:构造函数使用 this 关键字给所有属性和方法赋值。使 ClassA 构造函数成为 ClassB 的方法属性,ClassB调用这个方法 就可以访问到 ClassA 构造函数中定义的属性和方法。
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function () {
alert(this.color);
};
}
function ClassB(sColor, sName) {
this.newMethod = ClassA;
this.newMethod(sColor);//创建类A对象
delete this.newMethod;
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
有趣的是,对象冒充可以支持多重继承。也就是说,一个类可以继承多个超类。因为可以在一个类中定义多个属性,分别指向不同类的构造函数。
function ClassZ() {
this.newMethod = ClassX;
this.newMethod();
delete this.newMethod;
this.newMethod = ClassY;
this.newMethod();
delete this.newMethod;
}
这里存在一个弊端,如果存在两个类 ClassX 和 ClassY 具有同名的属性或方法,ClassY 具有高优先级,因为它从后面的类继承,会覆盖掉前面的同名属性。
ECMAScript 的第三版为 Function 对象加入了两个方法: call() 和 apply(),用于实现对象冒充继承:
call() 方法:第一个参数用作 this 的对象,其他参数作为实参传递给函数对象自身。
function ClassB(sColor, sName) {
ClassA.call(this, sColor);//继承ClassA,调用A的构造函数
//定义classB的新属性与新方法
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
apply() 方法:有两个参数,第一个参数用作 this 的对象,第二个参数是要传递给函数对象的实际参数的数组。
function ClassB(sColor, sName) {
ClassA.apply(this, new Array(sColor));//第二个参数把要传递给A构造函数的实参封装进数组
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
2)原型链方式
把类A的实例赋值给类B的prototype 属性,然后再在prototype对象上添加类B的新属性与新方法:
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
alert(this.name);
};
原型链的弊端是不支持多重继承。并且,原型链会用另一类型的对象重写类的 prototype 属性。
3)混合方式 (常用)
用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法:
//构造函数方式定义类A的非函数属性
function ClassA(sColor) {
this.color = sColor;
}
//原型方式定义类A的函数属性
ClassA.prototype.sayColor = function () {
alert(this.color);
};
//构造函数方式定义类B
function ClassB(sColor, sName) {
//在类B中使用对象冒充继承类A的非函数属性
ClassA.call(this, sColor);
this.name = sName;
}
//使用原型链继承A的函数属性
ClassB.prototype = new ClassA();
//使用原型方式定义类B的新函数属性
ClassB.prototype.sayName = function () {
alert(this.name);
};