在ECMAScript中,引用类型是一种数据结构。常被称为类但是不妥当。
1.Object类型
创建
创建Object实例的方式有两种
var person = new Object();
person.name = "Nicholas";
person.age = 29;
和我个人比较习惯的
var person = {
name : "Nicholas",
age : 29
}
访问
一般使用点表示法,不过支持使用方括号来表示,如下
alert(person["name"]);
alert(person.name);
但是,方括号法有着自己的优势:
- 方括号法可以通过变量来访问属性
var propertyName = "name";
alert(person[propertyName]);
如果属性名中包含着会导致错误的字符,或者关键字和保留字,也可以使用这个
person["first name"] = "chen";
尽管如此,我们还是尽量使用点表示法
2.array类型
ECMAScript中的数组与其它语言的数组有着很大的不同。ECMAScript中的数组可以保存任何类型的数据,而且大小可以动态调整。
创建
创建方式有两种,像这样
// 使用 Array 构造函数, new 可以省略
var colors = new Array();
var colors = new Array(20); // 知道数组要保存的数量,length为20、
var colors = new Array("red", "blue", "green"); // 知道要保存的项
还有一种我比较习惯的数组字面量表示法
var colors = ["red", "green", "blue"];
// 以下方式这辈子都不要去用
var values = [1,2,];
var options = [,,,,,];
读取和设置
使用方括号和索引值来访问所要的值。
var colors = ["red", "green", "blue"];
colors[3] = "brown"; // 这种情况数组会自动增加到该索引值加一的长度
数组的长度保存在length中,居然不是只读的。所以我们可以通过修改这个值直接舍弃掉数组末尾的值。而由于length始终指向最后一项加1的位置,我们可以很方便的添加新的项。
- 数组的检测
虽然不知道为什么,但是从ECMAScript3作出规定以后,确定某个对象是不是数组就成了一个经典问题。使用 instanceof 可以得到一个相对满意的结果。
但是,如果一个网页中存在多个框架,实际上就会有两个以上的不同的全局执行环境。就会存在两个以上不同的Array构造函数。所以,我们用isArray方法
if (Array.isArray(value)) {
// 对数组执行操作
}
栈方法
ECMAScript为数组专门提供了 push()和pop()方法,以便实现类似栈的行为。队列方法
ECMAScript提供了shift()方法来弹出第一项。
而unshift()方法则可以向数组的最前面添加数据。重排序方法
数组中存在着两个直接用来重排序的方法。 reverse()和 sort()。
reverse():会反转数组的顺序。
var values = [1, 2, 3, 4, 5];
values.reverse();
console.log(values); // 5,4,3,2,1
sort(): 默认情况下,按照升序排列数组。
原理:sort()调用每个数组项的toString()方法,然后比较得到的字符串,即使每 一项都是数字。所以我们经常看到数字“10”排在“5”前面。
所以我们不直接使用,而是为sort指定一个比较函数。
function compare(val1, val2) {
if (val1 < val2) {
return -1;
} else if (val1 > val2) {
return 1;
} else {
return 0;
}
}
// 使用
var vals = [0, 5, 1, 10, 15];
vals.sort(compare);
console.log(vals); // 0,1,5,10,15
- 操作方法
我们有着很多操作已经包含在数组中的项的方法。
concat():可以基于当前数组中的所有想创建一个新的数组。
var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);
console.log(colors); // red,green,blue
console.log(colors2); // red,green,blue,black,brown
slice():能够基于当前数组中的一个或者多个项创建一个新数组。slice()方法可以接收一个两个参数,作为返回项的起始和结束位置。
var colors = ["red", "green", "blue", "yellow", "purple"];
var colors1 = colors.slice(3); // yellow,purple
var colors2 = colors.slice(3,4); // yellow
splice():很厉害的一个数组,用法如下:
~删除:可以删除任意数量的项,两个参数,要删除的第一项和要删除的项数
~插入:可以像指定位置插入任意数量的项,三个参数,起始位置,0(要删除的项数)和要插入的项,如果插入的项数更多,只需要在后面多提供几个参数即可。
~替换:可以向指定位置插入任意数量的项,同时删除任意数量的项。指定三个参数,起始位置,删除项数,要插入的项。
splice()方法始终返回都是数组,这个数组包含的是从原始数组中删除的项。
var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1);
removed = colors.splice(1, 0, "yellow", "orange");
removed = colors.splice(1, 1, "red", "purple");
- 位置方法
有两个位置方法:indexOf() 和 lastIndexOf(),连个函数都接受两个参数:要查找的项以及(可选的)表示查找起点位置的索引。indexOf()从数组的开始向后查找,另一个从后面向前开始查找。返回的都是要查找的项在数组中的位置,如果没找到则返回-1。查找条件是 ===;
- 迭代方法
有五个迭代方法:每个函数都接收两个参数:要在每一项上运行的函数以及(可选)运行该函数的作用域对象——影响 this 的值。传入这些方法的函数会接收三个参数:数组项的值,该项在数组中的位置以及数组对象本身。
- every():对数组中的每一项运行给定函数,如果每一项都是true,返回true
- filter():对数组中的每一项运行给定函数,返回由会返回true的项所构成的数组
- forEach():对数组中的每一项运行给定函数,这个方法没有返回值
- map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组
- some(): 对数组中的每一项运行给定函数,如果该函数中的任一项返回true,则返回true
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var everyResult = numbers.every(function(item, index, array) {
return (item > 2);
});
console.log(evertReslut); // false
var someResult = numbers.some(functiom(item, index, array) {
return (item > 2);
});
console.log(someResult); // true
var filterResult = numbers.filter(function(item, index, array) {
return (item > 2);
})
console.log(filterResult); // [3, 4, 5, 4, 3]
var mapResult = numbers.mao(function(item, index, arrat) {
return item * 2
})
console.log(mapResult); // [2, 4, 6, 8, 10, 8, 6, 4, 2]
number.forEach(function(item, index,array){
// 执行某些操作
});
- 归并方法
有两个归并方法,reduce()和reduceRight()。这两个方法会迭代数组的所有项,然后构建一个最终返回的值。reduce从前开始遍历,另一个则是从最后开始。
两个方法都接受两个参数:一个在每一项上调用的函数和(可选)作为归并基础的初始值。所调用的函数接收四个参数:前一个值,当前值,项的索引值,和数组对象。这个函数的返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项,第一个参数是数组的第一项。
var values = [1, 2, 3, 4, 5];
var sum = values.reduce(function(prev, cur, index,array) {
return prev + cur;
})
console.log(sum); //15
3.Date类型
ECMAScript中的Date类型是在Java.util.Date类基础上构建的。Date使用从1970年1月1日到现在的毫秒值来保存日期。
创建
var now = new Date();
不传参的情况下,自动获取当前日期和时间。
但是要是想要根据特定的日期和时间创建对象,我们就需要计算从那个时间到我们要的时间所经过的毫秒值。为了简化这个过程,有Date.parse()方法和Date.UTC()
Date.parse():接收一个日期的字符串参数,然后返回相应日期的毫秒数。
字符串格式:
- 6/13/2004
- january 12,2004
- Tue May 25 2004 00:00:00 GMT-0700 // 最后一个代表时区
不能解析为日期,该方法返回一个NaN
实际上直接将表示日期的字符串传给Date,也会自动调用Date.parse()方法
var someDate = new Date("May 25, 2004");
Date.UTC():其实这个方法差不多,不过就是传入的参数很多
// 月0~11,小时0~23,天1~31
var allFives = new Date(2007, 6, 7, 7, 7, 7); // GMT时间2007年7月7日下午 7:07:07
继承的方法
Date类型也重写了toString(),toLocaleString(),valueOf()等方法。
每个方法在每个浏览器的具体表现并不一样.日期格式化方法
这一部分还请翻阅p101日期、时间组件方法
这一部分还请翻阅p102
4.RegExp类型
ECMAScript通过RegExp类型来支持正则表达式。使用下面类似Perl的语法,就可以创建一个正则表达式:
var expression = / pattern / flags ;
pattern:简单或是复杂的正则表达式
flags:正则表达式所带的标志,用以标明正则表达式的行为,有以下3个标志
- g: 表示全局模式,即模式将被应用于所有字符串,而非在第一个匹配项是立即停止
- i:表示不区分大小写,确定匹配项时忽略模式与字符串的大小写
- m:表示多行匹配,即在到达一行文本末尾时还会继续查找下一行是否存在与模式匹配的项
// 匹配字符串中所有“at”的实例
var pattern1 = /at/g;
// 匹配第一个“bat”或“cat”,不区分大小写
var pattern2 = / [bc]at /i;
// 匹配所有以“at”结尾的3个字符的组合,不区分大小写
var pattern = / .at /gi;
所有的元字符使用都应该先转义:( [ { \ ^ $ | ) ? * + . ] }
//匹配第一个"[bc]at",不区分大小学
var pattern2 = / \[bc\]at /i;
还有一个我个人不推荐使用的,利用RegExp构造函数创建正则表达式
// 下面两个正则表达式完全等价
var pattern1 = / [bc]at /i;
var pattern2 = new RegExp("[bc]at", "i");
使用正则表达式字面量和使用RegExp构造函数创建的正则表达式不一样,在ECMAScript3中正则表达式字面量始终会共享一个RegExp实例,而是用构造函数创建的每一个新RegExp都是一个新实例。但是正在ECMAScript5中改了过来。
RegExp实例属性
RegExp每个实例都有着下列属性:
- global:布尔值,表示是否设置了g
- ignoreCase:布尔值,表示是否i
- lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从0开始算
- multiline:布尔值,表示是否设置了m标志
- source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。
RegExp实例方法
RegExp对象的主要方法是exec(),该方法专门为捕获组而设计。
RegExp接受一个参数,返回包含第一个匹配项信息的数组;或者没有匹配到返回null;返回的数组虽然是Array,但有两个额外的属性:index和input。index是匹配项在字符串中的位置,input是应用正则表达式的字符串。
var text = "mom and dad and baby";
var pattern = "/ mom ( and dad( and baby)?)?/gi";
var matchs = pattern.exec(text);
console.log(matchs.index); // 0
console.log(matchs.input); // "mom and dad and baby"
console.log(matchs[0]); // "mom and dad and baby"
console.log(matchs[1]); // " and dad and baby"
console.log(matchs[2]); // " and baby"
RegExp实例的另一个方法是test()。
该方法接受一个字符串,当该模式与字符串匹配时返回true。
RegExp构造函数属性
RegExp构造函数本身自带有一些属性
长属性名 | 短属性名 | 说明 |
---|---|---|
input | $_ | 最近一次匹配要匹配的字符串 |
lastMatch | $& | 最近一次匹配的匹配项 |
lastPaern | $+ | 最近一次匹配的捕获组 |
leftContext | $` | input字符串中lastMatch之前的文本 |
multiline | $* | 布尔值,表示是否所有表达式都是用多行横式 |
rightContext | $' | 表示input字符串中lastMatch之后的文本 |
除此之外,我们还可以用9来表示存储于第一到第九个匹配的捕获组。
模式的局限性
ECMAScript所不支持的:
- \a 和 \z
- 向后查找
- 并集和交集
- 原子组
- Unicode支持
- 命名的捕获组
- s 和 x 匹配模式
- 条件匹配
- 正则表达式注释
5.Function类型
函数实际上是个对象,每个函数都是Function类型的实例。而且和其他引用类型一样都具有属性和方法。
// 实际上
function sum (num1, num2) {
return num1 + num2;
}
// 和
var sum = function(num1, num2) {
retrun num1 + num2;
};
// 定义函数的方式相差无几
还有一种函数定义的方法,非常不推荐,但是在这里提一下
// 这种函数定义的方法会导致解析两次代码。
var sum = new Function("num1", "num2", "return num1 + num2");
没有重载!!!!!!!!!!!!
再说一遍,没有重载,两个同名函数,结果会是后面的函数覆盖了前面的结果。
函数声明与函数表达式
我们要学会区分函数声明与函数表达式。因为解析器对函数声明与函数表达式并非一视同仁。解析器会先读取函数声明,使他可以被访问,而函数表达式则等到执行到它所在的代码行才会真正被解析执行。
// 这段代码是可以执行的,函数声明
alert(sum(10, 10));
function sum(num1, num2) {
return num1 + num2;
}
// 但是像这样是错误的,函数表达式
alert(sum(10, 10));
var sum = function(num1, num2) {
return num1 + num2;
}
代码在开始执行之前,解析器已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境当中。
作为值的函数
因为ECMAScript函数名本身就是变量,所以函数名也可以作为值来使用。
// 这里将会出现一段比较“邪门”的代码
function callSomeFunction(someFunction, someArgument) {
return someFunction(someArgument);
}
function add10(num){
return num + 10;
}
var result1 = callSomeFunction(add10, 10);
console.log(result1); // 20
函数内部属性
在函数内部,有两个特殊的对象:this和arguments。
arguments虽然主要用途是保存函数参数,但是这个对象还有一个名叫callee的属性,这个属性是一个指针,指向拥有这个arguments对象的函数。严格模式下访问arguments.callee会导致错误。
例如:
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return factorial(num - 1) * num;
}
}
// 上面的函数为了实现函数体与函数名的松耦,可以像这样写
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num - 1);
}
}
// 这样我们就可以使用别的函数名来调用这个函数
var trueFactorial = factorial;
console.log(trueFactorial(5));
this也很特殊。尤其是和计时器相结合以后,这一点以后再谈。
this引用的是函数执行的环境对象。
此外再多提一个caller,这个属性中保存着调用当前函数的函数的引用。
function outer() {
inner();
}
function inner() {
console.log(arguments.callee.caller); // 显示outer的源代码
}
函数属性和方法
函数也是个对象,所以函数本身也有属性和方法。
属性中一个是 length 另一个是 prototype。
length表示函数希望接受的参数的个数。
prototype很重要,保存所有引用类型实例方法。实际上什么toString()和valueOf()方法都是保存在它的下面。prototype在ECMAScript中不可枚举。
有两个方法 apply() 和 call().
apply()方法有两个参数一个是运行函数的作用域,另一个是参数数组。
call()方法和apply()的作用一样,不过接收参数的方式不同。
function sum(num1, num2) {
return num1 + num2;
}
function callsum1(num1, num2) {
return sum.apply(this, arguments);
}
function callsum2(num1, num2) {
return sum.apply(this, [num1, num2]);
}
function callsum3(num1, num2) {
return sum.call(this, num1, num2);
}
console.log(callsum1(10, 10)); // 20
console.log(callsum2(10, 10)); // 20
console.log(callsum3(10, 10)); // 20
ECMAScript5中还定义了一个方法bind():创建一个函数实例,并将this绑定到传给bind()函数的值
window.color = "red";
var o = { color: "blue" };
function sayColor() {
console.log(this.color);
}
var objSayColor = sayColor.bind(o); // 创建一个函数,该函数的this值等于o
objSayColor(); // 显示 “blue”
6.基本包装类型
为了便于操作基本类型值,ECMAScript还提供了3个特殊的引用类型。Boolean、 Number、 String。实际上每创建一个基本类型值得时候,都会在后台创建一个对应的基本包装类型的对象。
对所有的基本包装类型调用typeof返回的都是“object”。
对所有的基本包装类型在转化为布尔类型值时都是true。
boolean对象用处不大
Number类型
这种类型有个方法toFixed(),指定返回的数有几位小数。
还有toExponential(),返回以指数表示法显示的数字。
String类型
- length属性:返回字符串长度
- charAt()和charCodeAt():接收一个参数,基于0的字符位置。charAt()返回给定位置的字符。charCodeAt()返回给定位置字符的编码。我们可以像访问数组一样用方括号加数字的方法访问特定位置的数字。
- concat():将一个或者几个字符串拼接起来
- slice(): 参数1:指定字符串起始位置,参数2:指定结束位置。
- subString():参数1:指定字符串起始位置,参数2:指定结束位置。
- subStr():参数1:指定字符串起始位置,参数2:指定几个字符。
- indexOf():从一个字符串中搜索一个字符串的位置。
- lastIndexOf():从一个字符串中搜索一个字符串的位置,从后向前。
- trim():这个方法会创建一个字符串的副本,删除前置和后缀的所有空格。
- toLowerCase():转成小写
- toUpperCase():转成大写
- match():本质上和代用RegExp的exec()一样
- search():查找字符串第一次出现的位置
- replace():替换部分的字符串。第二个参数也可以是个函数
- htmlEscape():可以转义四个字符,小于号,大于号,和好以及双引号。
- split():用指定的符号将字符串分隔成多个子字符串。
- localeCompare(): ~字符串在字母表中的排在字符串参数前返回一个负数(-1) ~相等返回0 ~在他后面则返回一个正数
- fromCharCode():接收一个或者多个字符编码,然后将它们转换成一个字符串。不常用
7.单体内置对象
什么是内置对象:有ECMAScript实现提供的,不依赖于宿主环境的对象,这些对象在程序执行之前就已经存在了。除了Object、Array、String以外还有Global和Math。
Global对象
不属于任何其他对象的属性和方法,最终都属于他的属性和方法。
- URI编码方法
encodeURI():主要用于整个URI编码
encodeURIComponent():则会对他发现的任何非标准字符进行编码。 - eval方法
eval()非常强大。只接受一个参数。即要执行的ECMAScript字符串
eval("alert('hi')");
// 等价于
alert("hi");
在eval中创建的任何函数和变量都不会得到提升;使用eval尽量小心,谨防代码注入。
- Global对象的属性
请翻阅p133 - window对象
Math对象
ECMAScript为保存数学公式和信息提供了一个公共位置即Math对象。
- math对象的属性
属性 | 说明 |
---|---|
Math.E | e |
Math.LN10 | 10的自然对数 |
Math.LN2 | 2的自然对数 |
Math.LOG2E | 以2为底e的对数 |
Math.LOG10E | 以10为底e的对数 |
Math.pi | π |
Math.SQRT1_2 | 1/2的平方根 |
- min()和max()方法
要找到数组中的最大值
var values = [1, 2, 3, 4, 5, 6, 7, 8];
var max = Math.max.apply(Math, values);
- 舍入方法
Math.ceil():向上舍入
Math.floor():向下舍入
Math.round():标准舍入 - random()方法:返回一个大于等于0小于1的一个随机数。
var num = Math.floor(Math.random() * 10 + 1); // 返回一个1~10的随机数