第五章、引用类型
1、创建Object的方式有两种
第一种是使用new操作符: varperson = new Object();
第二种是使用对象字面量表示法:var person = {name : ‘test’}; (注意最后一个属性后面不要加逗号,否则在某些浏览器中可能会出现错误),在使用字面量创建对象时,属性名也可以使用字符串:var person = {“name”:”test”};
2、访问对象属性时可以使用点表示法,也可以使用方括号表示法来访问对象的属性:
var person = {“name”:”test”};
alert(person.name);
alert(person[“name”]);
这两种方式没有太大区别,但是使用方括号语法可以通过变量来访问属性。
1、 Array类型:
var arr1 = new Array();
var arr2 = Array(1, 2, 3);
var arr3 = [];
2、 栈方法:
ECMAScript提供了一种让数组的行为类似于其他数据结构的方法,例如栈和队列
var stack = [];
for( var i = 0; i< 10; i++ ){
stack.push(i );
}
console.log( stack.join("-") );
for( var i = 0; i < 5; i++ ){
console.log("pop: " + stack.pop() );
}
3、 队列方法:
队列方法在队列的末尾添加项,而在队列的前端移除项
var queue = [];
for( var i = 0; i < 10; i++ ){
queue.push( i );
}
console.log( queue.join("-") );
for( var i = 0; i < 5; i++ ){
console.log( "shift: " + queue.shift() );
}
unshift的作用与shift相反,能在数组的前端添加任意多个项并返回数组的长度。
4、 数组函数
sort可以完成对数组的排序,reverse会翻转数组项的顺序。
与大多数排序函数类似,sort可以接受一个比较函数,从而实现对数组的自定义排序:
concat(): 基于原来的数组拼接新的数组:
var colors = ["red","green" ];
console.log( colors.concat(["blue","yellow"]) );
slice(): 获取数组的一部分作为新的数组:
var arr = [1, 2, 3, 4, 5];
console.log( arr.slice(2, 4));
splice数组:数组中插入、删除或者替换项:
(1)、删除
var colors = ['r', 'g', 'b'];
var removed = colors.splice(0, 2);
console.log(removed);
(2)、添加
var colors = ['r', 'g', 'b'];
var removed = colors.splice(1, 0, "added");
console.log(colors);
(3)、替换
var colors = ['r', 'g', 'b'];
var removed = colors.splice(1,1 , "redd","blue");
console.log(removed);
8、Date类型
Date类型的valueOf方法根本不返回字符串,而是返回日期的毫秒表示。因而可以直接用比较操作符直接比较日期值:
var date1 = new Date(2007, 1, 1);
var date2 = new Date(2007, 1, 2);
console.log( date1 < date2 );
Date的组件方法,参考文档,比较多,不细列出。比较重要的有:
getTime() : 返回表示日期的毫秒数,与valueOf一致。
setTime(): 设置日期
getFullYear(): 取得四位数的年份
getMonth(): 取得月份
getDate(): 返回月份中的天数
……
9、RegExp类型
ECMAScript通过RegExp类型来支持正则表达式。
varpattern = /pattern/flags
flags有三种:
g:表示全局匹配模式,模式被应用于整个字符串,而不是发现第一个匹配项时停止
i:不区分大小写的匹配
m:多行模式,到达一行文本结尾时还会继续查找下一行的文本。
另一种创建正则表达式的方式是通过RegExp对象:
var pattern = newRegExp("[bc]at", "i");
传递给RegExp的两个参数都是字符串,不能把正则表达式的字面量直接传递给RegExp。由于RegExp构造函数的模式参数是字符串,因而在某些情况下需要对字符进行双重转义,所有元字符都必须双重转义。
RegExp的实例都具有如下的一些属性:
(1) global表明是否启用了g标志
(2) ignorecase是否启用了i标志
(3) lastIndex表示搜索下一个匹配项的字符位置,从0开始
(4) multiline 是否启用了m标志
(5) source 正则表达式的字符串表示,按照字面量形式返回。
var pattern1 = /\[bc\]at/i;
var pattern2 = newRegExp("\\[bc\\]at", "i");
console.log(pattern1.global);
console.log(pattern1.ignoreCase);
console.log(pattern1.multiline);
console.log(pattern1.lastIndex);
console.log(pattern1.source);
console.log(pattern2.source);
RegExp对象有两个常见实例方法:exec() 和 test();
exec专为捕获组而设计的,exec接收一个字符串参数,返回包含匹配项的数组:
test()接收一个字符串参数,在参数与正则表达式匹配时返回true, 否则返回false
ECMAScript正则表达式不支持的特性:
(1)、匹配字符串开始和结束的\A和\Z锚点
(2)、向后查找lookbehind
(3)、并集和交集类
(4)、原子组
(5)、Unicode支持(单个字符除外)
(6)、命名的捕获组
(7)、s和x匹配模式
(8)、条件匹配
(9)、正则表达式注释
10、Function类型
每个函数都是Function函数的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上是一个指向函数对象的指针。
由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同,一个函数可以有多个名字:
function sum(a, b){
returna + b;
}
var antSum = sum;
console.log( antSum(1, 2) );
sum = null;
console.log( antSum(1, 2) );
将函数名想象成指针,也就解释了为什么ECMAScript中没有函数重载的概念。
解析器在加载数据时,对于函数声明和函数表达式并非一视同仁:解析器会率先读取函数声明,并使其在执行任何代码之前可用;而对于函数表达式,则必须等到解析器执行到它所在的代码行时才会真正被解释执行,如下的方式在执行期会导致错误:
console.log( sum(1, 2) );
var sum = function(a, b){
returna + b;
}
由于ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样吧一个函数传递给另一个函数,而且可以把一个函数作为另一个函数的结果返回。从这个意义上说,函数是ECMAScript中的一等公民。
可以从一个函数中返回函数,例如创建一个根据属性名进行比较的函数(用于排序):
function createCompare( prop ){
return function( obj1, obj2 ){
var v1 = obj1[ prop ];
var v2 = obj2[ prop ];
if( v1 < v2 ){
return -1;
}else if( v1 > v2 ){
return 1;
}else{
return 0;
}
};
}
注意这里如果使用return v1 – v2;则是错误的,因为对于字符串而言, 字符串相减返回的结果是NaN。
函数内部,有两个特殊的属性:this和arguments。其中arguments是一个类数组对象,其中包含了传入函数的所有参数:
functiontest(){
console.log( arguments );
console.log(arguments.length );
}
test(1,2, 3, 4, 5);
虽然arguments的主要作用是保存函数参数,但是这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对
象的函数:
function factor( num ){
if( num <= 1 ){
return 1;
}
return num * arguments.callee( num - 1 );
}
使用arguments.callee可以保证无论函数使用什么名字,都能正确完成递归(解除耦合)。
函数内部另一个特殊对象是this,this是函数在执行时所处的作用域(当前网页的全局作用域中调用函数时,this对象引用的就是window)
函数属性和方法:ECMAScript函数是对象,因此函数也有属性和方法,每个函数包含两个属性:length和prototype,其中,length表示函数希望接收的命名参数的个数:
function sum(a, b){
return a + b;
}
console.log(sum.length);
prototype可以说是ECMAScript中最重要的属性了(详见第六章)
每个函数都包含两个继承而来的方法:apply和call,这两个方法的用途是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值:
function sum(a, b){
return a + b;
}
function callSum(a, b){
//函数是在全局作用域被调用的,因此this == window
return sum.apply(this, arguments);
}
function callSum2(a, b){
return sum.apply( this, [a, b] );
}
console.log(callSum(1, 2));
console.log(callSum2(2, 3));
call方法和apply方法作用相同,它们的区别在于接收参数的方式不同:
function callSum(a, b){
return sum.call(this, a, b);
}
console.log(callSum(1, 3));
实际上,传递参数并非call和apply真正的用武之地,它们真正强大的地方在于能够扩充函数赖以运行的作用域。:
var color = "red";
var o = {color : "blue"};
function sayColor(){
console.log(this + " this.color: " + this.color);
}
sayColor();
sayColor.call(this);
sayColor.call( o );
用call和apply来扩从作用域的最大好处,就是对象不需要与方法有任何耦合关系
11、基本包装类型
为了便于操作基本类型值,ECMAScript还提供了3个特殊的引用类型:Boolean、Number和String.
引用类型和基本包装类型的区别在于对象的生存期,使用New操作符创建的引用类型的实例,在执行流离开当前作用域之前一直都保存在内存中,而自动创建的自动包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。这意味着不能为基本类型值添加属性和方法:
vars = "some test";
s.color= "red";
console.log(s.color );//undefined
不建议显式地创建基本包装类型的对象。
toFixed和 toExponential,toPrecision 两个用于格式化数值的方法。
使用typeof操作符测试基本类型数值时,始终会返回"number",而在测试Number对象时,则会返回object. Number对象是Number类型的实例,而基本类型的则不是。
String类型:
String类型的每个实例都含有一个length属性,表示字符串包含多少个字符(多字节字符依然算一个字符):
var s = "some test好";
console.log( s.length );
charAt和charCodeAt用于访问字符串中的特定字符。其中charAt返回给定位置的单个字符,而charCodeAt则返回的是指定位置的字符编码:
vars = "hello";
console.log(s.charAt(2));//l
console.log(s.charCodeAt(2));//108
concat用于将一个或者多个字符串拼接起来,返回拼接到的新字符串。
slice, substr, substring: 基于子字符串创建新的字符串(它们都不修改原始字符串的值,而只是返回一个基本类型的字符串值):
string.slice(start, end);
string.substring(start, end);
string.substr(start, len);
indexOf返回子字符串的位置,如果没有找到,返回-1(向后搜索)
lastIndexOf返回子字符串最后一次出现的位置(向前搜索)
可以通过循环查找,找出所有子串在字符串中的所有位置:
字符串匹配:match和search
容易搞混的地方: test()和exec()是正则表达式的方法,RegExp.test(str), RegExp.exec(str);
而match和search是字符串的操作方法,str.match(RegExp), str.search(RegExp)
replace用于替换,第一个参数可以是RegExp正则表达式或者字符串,如果是字符串,那么只会替换第一个子字符串,如果要替换所有的字符串,可以提供一个正则表达式,而且指定全局标志(g).第二个参数可以将正则表达式操作得到的值插入到结果字符串中:
var text = "cat, bat, sat, fat",
result = text.replace(/(.at)/g, "word($1)");
console.log( result );
replace的第二个参数也可以是一个函数:在只有一个匹配项的情况下,会向这个函数传入3个参数:模式的匹配项、模式匹配项的起始位置和原始的字符串:
function htmlEscape( str ){
return str.replace(/[<>"&]/g, function( match, pos, orig){
switch( match ){
case "<":
return "<";
break;
case ">":
return ">";
break;
case "&":
return "&";
break;
case "\"":
return """;
break;
}
});
}
split用于模式分割:
var colorStr = "red, blue, green,red";
console.log( colorStr.split(","));
console.log( colorStr.split(",",2) );
fromCharCode接收一个或者多个字符编码,然后将他们转换为一个字符串:
var str = String.fromCharCode( 104, 101,108, 108, 111 );
console.log( str );
12、内置对象
Global对象:不属于任何其他对象的属性和方法,最终都是Global对象的属性和方法。实际上,没有全局变量或者全局函数,所有在全局作用域中定义的属性和函数,都是Global对象的属性。
URL编码方法:
Global对象的encodeURI和encodeURIComponent()方法可以对URI进行编码。
encodeURI主要用于对整个URI进行编码,而encodeURIComponent主要用于对URI的某一段进行编码。它们的主要区别在于,encodeURI不会对本身属于URI的特殊字符进行编码,例如冒号:,正斜杠/,问号?和井号#; 而encodeURIComponent则会对它发现的任何非标准的字符进行编码:
var uri = "http://www.prox.com/illvalue#start";
//http://www.prox.com/ill%20value#start
console.log( encodeURI(uri) );
//http%3A%2F%2Fwww.prox.com%2Fill%20value%23start
console.log( encodeURIComponent(uri) );
与encodeURI和encodeURIComponent对应的两个方法分别是decodeURI和decodeURIComponent,它们是一一对应的,decodeURI只能对使用encodeURI编码的字符进行解码,同样,decodeURIComponent只能对使用encodeURIComponent编码的字符进行解码。
eval:
eval中可以定义函数,这个函数可以被外部调用
eval(" functionsayHi(){console.log('hi');} ");
sayHi();
window对象: ECMAScript虽然没有指出如何直接访问Global对象,但Web浏览器都是将这个全局变量作为Window对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,都成了window对象的属性.
var color = "red";
function sayColor( ){
console.log(window.color);
}
window.sayColor();
Math对象:
Math对象的属性包括:
Math.E
Math.LN10
Math.LN2
Math.LOG2E
Math.LOG10E
Math.PI
Math.SQRT1_2
Math.SQRT2
Math对象的方法:
(1) min和max方法,用于确定一组数值中的最大和最小值。
(2) 舍入方法: Math.ceil(), Math.floor(), Math.round
(3) random()方法。返回0-1之间的随机数,不包含0和1,套用公式:
val = Math.floor(Math.random()* 可能的值总数 + 第一个可能的值)可以实现在一个range中选择一个值。
functionselectFrom(low, high){
var nums = high – low + 1;
return Math.floor( nums * Math.random() + low );
}
console.log(selectFrom(2,10));
(4) abs(num), exp(num), log(num),pow(num, power)