JS Pro - 引用类型
引用类型的值(对象)其实就是引用类型的一个实例。在ECMAScript里头,引用类型就是用来保存数据和方法的,别的语言称之为"类"(Class)。虽说ECMAScript是面向对象的语言,但在ECMAScript里,缺少面向对象的概念——类和接口。所以,ECMAScript里不能称为类,我们称为:对象定义。
Object类型
我们通过new操作符,后面跟一个构造函数来创建对象。构造函数简单来说就是一个用来创建新对象的方法。
var person = new Object();
创建对象的2种语法:
1. 使用new操作符:
var person = new Object(); person.name = “Nicholas”; person.age = 29;
2. 对象字面量(属性与值用":"隔开,每一对属性/值用","隔开)——推荐使用该方法,清晰、简洁:
var person = { name : “Nicholas”, age : 29 };
最好给属性值加上"",如:"name"。
还有一种特殊的方法:
var person = {}; //same as new Object() person.name = "Nicholas"; person.age = 29;
在你创建函数的时候,也可以使用对象字面量来定义。
你可以使用.或者[]来访问对象属性,如:
alert(person.name);
alert(person[name]);
两者基本相同,当使用[]时,我们可以使用变量来访问该属性。
Array类型
在ECMAScript当中的数组和其他语言里的数组,最大的差别在于,任意一个数组里可以包含任意类型的数据。而且ECMAScript的数组的大小是动态的。
创建数组的两个方法——1. 使用Array构造函数;2. 使用数字字面量表示法(当使用该方法时,不会调用Array构造函数)
//1st method var colors = new Array(); //And you can omit the new operator in this way var colors = Array(); //2nd method, use [] var colors = ["red", "blud", "yellow"];
使用[]访问数组内的元素,从0开始。
注意:array.length不是只读的,所以可以通过修改length的值,从数组中移除数据,如:
var colors = [“red”, “blue”, “green”]; //creates an array with three strings colors.length = 2; alert(colors[2]); //undefined
可以使用length,往数组的末端添加数据。
var colors = [“red”, “blue”, “green”]; //creates an array with three strings colors[colors.length] = “black”; //add a color (position 3) colors[colors.length] = “brown”; //add another color (position 4)
数组-栈方法(Stack Method):
栈是后进先出结构(LIFO--Last-In-First-Out),最新添加的项,最早被移除。
push():接受任意数量的参数,逐个添加到数组末尾,返回修改后数组的长度。
pop():从数组末尾移除最后一项,减少数组的length值,返回移除的项。
数组-队列方法(Queue Method):
队列数据结构的访问是先进先出(FIFO--First-In-First-Out)。队列在列表的末端添加项,从列表的前段移除项。
shift():移除数组中的第一个项并返回该值。
unshift():往数组第一位添加项,返回修改后的数组长度。
数组-重排序方法(Reordering Method):
reverse():纯粹的倒序
sort():按升序排序——它会调用每一项的String()方法,然后进行比较。因为它是基于字符的比较,所以如果数组内元素为数字时,就会有问题了。
var values = [0, 1, 5, 10, 15]; values.sort(); alert(values); //0,1,10,15,5
虽然5比10小,但在字符串比较中,10在5的前面,所以最终结果反而被打乱了。
但你可以通过给sort()传进一个比较函数(compare())来使它输出正确的值。
function compare(value1, value2) { if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } }
var values = [0, 1, 5, 10, 15]; values.sort(compare); alert(values); //0,1,5,10,15
reverse()和sort()都是返回重新排序后的数组。
数组-操作方法(Manipulation Method):
concat()函数:基于当前数组的所有项,创建一个新数组。
var colors = [“red”, “green”, “blue”]; var colors2 = colors.concat(“yellow”, [“black”, “brown”]); alert(colors); //red,green,blue alert(colors2); //red,green,blue,yellow,black,brown
slice()函数:基于当前数组中的一项或多项创建一个新数组。它接收1或2个参数。第一个是起始位置,第二个是结束位置。
var colors = [“red”, “green”, “blue”, “yellow”, “purple”]; var colors2 = colors.slice(1); var colors3 = colors.slice(1,4); alert(colors2); //green,blue,yellow,purple alert(colors3); //green,blue,yellow
concat()和slice()都不会改变原数组的值。
splice()函数:这个函数可以做3件事情,
1. 删除元素(提供2个参数,arg[0]: 第一个要删除的元素位置;arg[1]: 总共要删除的元素的个数):splice(0, 2) 删除头两个元素
2. 插入元素(提供3个或以上参数,arg[0]: 插入的起始位置;arg[1]: 要删除的元素个数(可为0);arg[2~N]:要添加的元素 ):splice(1, 0, "red", "green")
3. 替换元素(插入语法的变种:改变删除元素的个数,达到替换的目的):splice(1, 2, "red", "green")
var colors = [“red”, “green”, “blue”]; var removed = colors.splice(0,1); //remove the first item alert(colors); //green,blue alert(removed); //red - one item array removed = colors.splice(1, 0, “yellow”, “orange”); //insert two items at position 1 alert(colors); //green,yellow,orange,blue alert(removed); //empty array removed = colors.splice(1, 1, “red”, “purple”); //insert two values, remove one alert(colors); //green,red,purple,orange,blue alert(removed); //yellow - one item array
正则表达式类型(RegExp Type):
语法:
var expression = /pattern/flags;
Function类型(Function Type):
每个函数都是Function类型的实例,而且与其他引用类型一样具有属性和方法。由于函数是对象,所以函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
定义函数的语法:
function sum (num1, num2) { return num1 + num2; }
或者
var sum = function(num1, num2){ return num1 + num2; };
使用不带圆括号的函数名是访问函数指针,而非调用函数:
function sum(num1, num2){ return num1 + num2; } alert(sum(10,10)); //20 var anotherSum = sum; alert(anotherSum(10,10)); //20 sum = null; alert(anotherSum(10,10)); //20
没有重载!
function addSomeNumber(num){ return num + 100; } function addSomeNumber(num) { return num + 200; } var result = addSomeNumber(100); //300
函数声明与函数表达式的区别:
函数声明:
function newFunction() {};
函数表达式:
var newFunction = function() {};
解析器会率先读取函数声明,所以在执行代码前就已经生效(可以访问)。而函数表达式,则必须等到解析器运行到所在代码行才开始解释执行。所以,以下的第一段代码可以运行成功,第二段代码会报错。
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); alert(result1); //20
function getGreeting(name){ return “Hello, “ + name; } var result2 = callSomeFunction(getGreeting, “Nicholas”); alert(result2); //”Hello, Nicholas”
函数内部属性:
每个函数内部都包含2个特殊对象:arguments和this。
arguments对象:一个类数组对象,包含传入函数的所有参数。argument对象还包含一个callee属性,该属性是一个指针,指向拥有这个arguments对象的函数。
我们用递归函数来解释一下这个属性。
function factorial(num){ if (num < = 1) { return 1; } else { return num * factorial(num-1) } }
上面是一个很经典的递归函数,在函数有名字而且不改变的情况下,肯定是不会有问题。
下面,我们试一下用arguments.callee这个属性重写上面的函数。
function factorial(num){ if (num < = 1) { return 1; } else { return num * arguments.callee(num-1) } }
var trueFactorial = factorial;
factorial = function(){ return 0; };
alert(trueFactorial(5)); //120 alert(factorial(5)); //0
通过使用arguments.callee重写的函数,在调用时就不再是针对factorial这个函数了。如上所示,在使用arguments.callee定义完factorial函数后,我们又定义了一个新的变量(trueFactorial),并赋予factorial的值(就是指向factorial函数的另一个变量);然后我们重新定义了factorial函数,使它返回0。
由于现在原本的factorial函数体已经和函数名的关系已经解除,它只和调用它的那个函数名相关,所以trueFactorial能够返回正确的值。而factorial现在就只有唯一的返回值:0。
window.color = “red”; var o = { color: “blue” }; function sayColor(){ alert(this.color); } sayColor(); //”red” o.sayColor = sayColor; o.sayColor(); //”blue”
函数属性与方法:
ECMAScript里头的函数其实就是对象,因此函数也有属性和方法。每个函数会有2个属性:length和prototype。
length:希望接受的已命名的参数个数
function sayName(name){ alert(name); } function sum(num1, num2){ return num1 + num2; } function sayHi(){ alert(“hi”); } alert(sayName.length); //1 alert(sum.length); //2 alert(sayHi.length); //0
prototype:对于ECMAScript中的引用类型而言,prototype就是保存他们所在实例方法的实际位置。(稍后详细解释!)
每个函数又都包含2个非继承方法:apply()和call()。这两个方法的作用都是在特定的作用域中调用函数,实际上等于设置函数体内的this对象的值。
apply()和call()的第一个参数都是作用域,唯一的不同就是:apply()可以使用arguments对象或者数组形式传递参数,而call()则必须明确列出每一个参数。
这两个方法最强大的地方在于他们能够扩充函数赖以运行的作用域。我们来重写一下this对象里面的那个例子:
window.color = “red”; var o = { color: “blue” };
function sayColor(){ alert(this.color); }
sayColor(); //red sayColor.call(this); //red sayColor.call(window); //red sayColor.call(o); //blue
我们只定义了一个全局函数sayColor(),默认情况下,它的作用域是window,所以,call(this)和call(window)返回的都是red。在最后一行,我们使用call()函数设定运行环境为对象o,所以它返回的值为blue。
使用call()和apply()方法的最大好处是,不需要与方法有任何的耦合关系。在this函数的例子里,我们首先把sayColor()函数放在对象o中,然后再通过o来调用它。重写后,我们可以直接通过call来设定执行环境。
函数的继承方法toLocaleString()和toString(),他们总是返回函数的代码。另外,valueOf()也是返回函数的代码。
基本包装类型:
3个特殊的引用类型:Boolean,Number和String。这3个都是基本类型,但ECMAScript给他们创建了这些基本包装类型,使我们可以像操作对象一样操作他们(但一般不推荐这么使用)。
var s1 = “some text”; var s2 = s1.substring(2);
s1是一个基本类型值,为什么会有substring方法呢?其实,这背后还隐藏着3步:
1. 创建String类型对象 s1
2. 使用s1的substring方法
3. 把s1设置为null
var s1 = new String(“some text”); var s2 = s1.substring(2); s1 = null;
Number类型
toFixed():按照指定的小数位返回字符串(会四舍五入);常用于处理货币值。
var num = 10; alert(num.toFixed(2)); //”10.00” var num = 10.005; alert(num.toFixed(2)); //”10.01”
toExponential():返回指数表示法的数值字符串
var num = 10; alert(num.toExponential(1)); //”1.0e+1”
toPrecision():根据值来判断使用toFixed()或者toExponential()
var num = 99; alert(num.toPrecision(1)); //”1e+2” alert(num.toPrecision(2)); //”99” alert(num.toPrecision(3)); //”99.0”
由于一位数无法准确表达99,所以将它舍为100,然后使用指数形式显示;2位数时,当然就是99;3位数时,则显示为99.0。
String类型:
继承下来的valueOf() , toLocaleString() , and toString()方法,都是返回string的值。
length属性:返回字符串长度。注意:在ECMAScript里,即使字符串包含双字节字符,每个字符也仍然算一个字符。
字符方法:
charAt(): 返回指定位置的字符。
var stringValue = “hello world”; alert(stringValue.charAt(1)); //”e”
charCodeAt(): 返回指定位置字符的字符编码。
var stringValue = “hello world”; alert(stringValue.charCodeAt(1)); //outputs “101”
字符串操作方法:
concat():连接字符串,并返回新的字符串。该方法接受任意个参数,所以你可以使用它一次过连接多个字符串。不过,一般我们可以直接使用"+"来连接字符串。
var stringValue = “hello “; var result = stringValue.concat(“world”); alert(result); //”hello world” alert(stringValue); //”hello”
slice(), substr(), substring():接受一个或两个参数,他们的第一个参数都是指起始位置。
对于slice()和substring()而言,第二个参数是指结束的位置(这个位置不包含在返回的字符串里)
而substring()的第二个参数,则是表示总共要返回的字符个数。
var stringValue = “hello world”; alert(stringValue.slice(3)); //”lo world” alert(stringValue.substring(3)); //”lo world” alert(stringValue.substr(3)); //”lo world” alert(stringValue.slice(3, 7)); //”lo w” alert(stringValue.substring(3,7)); //”lo w” alert(stringValue.substr(3, 7)); //”lo worl”
当传入的第二个参数为负值时,表现就很不一样了。slice()会将传入的负值与字符串长度相加;substr()会将负的第一个参数加上字符串长度,将第二个负参数转换为0;最后,substring()会将所有传入的负值转换为0。
查找字符位置方法:
indexOf()和lastIndexOf(),他们都是在一个字符串里搜索指定的子字符串,返回子字符串的位置(当找不到时,返回-1)
indexOf()从前往后找,lastIndexOf()从后往前找。
它们都接受第二个参数,表示从字符串中的哪个位置开始搜索。indexOf()就是忽略前面的字符,找;lastIndexOf()就是忽略后面字符,往前找。
通过第二个参数,我们可以找出一个字符串里包含子字符串的位置。
var stringValue = “Lorem ipsum dolor sit amet, consectetur adipisicing elit”; var positions = new Array(); var pos = stringValue.indexOf(“e”); while(pos > -1){ positions.push(pos); pos = stringValue.indexOf(“e”, pos + 1); } alert(positions); //”3,24,32,35,52”
字符串改变大小写:
toLowerCase() , toLocaleLowerCase() , toUpperCase() , and toLocaleUpperCase() .
字符串模式匹配方法:
match()方法:该方法的返回值与正则表达式对象的exec()方法一致;它接受一个参数,可以是正则表达式或者RegExp对象。
var text = “cat, bat, sat, fat”; var pattern = /.at/; //same as pattern.exec(text) var matches = text.match(pattern); alert(matches.index); //0 alert(matches[0]); //”cat” alert(pattern.lastIndex); //0
localCompare()方法:比较两个字符串,并返回-1,0,1。(根据两个字符串的字母先后顺序)
fromCharCode()方法:接受字符编码, 将其转换为字符。其实就是charCodeAt()的反向方法。
内置对象:
ECMAScript里头有2个内置对象:Global和Math,在使用它们前,不需要显式地实例化。
Global对象:其实,所有在全局作用域中定义的属性和函数,都是Global对象的属性。
URI-Encoding方法:就是转换URI(Uniform Resource Identifiers)的方法。由于一个合法的URI是不能包含一些特殊字符的,例如空格,所以我们要先对其进行转换再传给浏览器。
encodeURI():作用于整个URI;encodeURIComponent()则对URI的某一段进行编码。他们的区别在于,encodeURI()不会对本身收URI的特殊字符进行编码,例如冒号、正斜杠、问好和井字符。而encodeURIComponent()则会对所有非标准字符进行编码。
var uri = “http://www.wrox.com/illegal value.htm#start”; //”http://www.wrox.com/illegal%20value.htm#start” alert(encodeURI(uri)); //”http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start” alert(encodeURIComponent(uri));
一般而言,encodeURIComponent()使用会更多,因为我们经常处理的是查询字符串参数,而不是整个URI。
和这两个方法对应的是decodeURI()和decodeURIComponent()。
eval()方法:只支持一个参数。当调用eval()方法时,解释器会把参数内的语句当做实际的ECMAScript语句来解析,然后执行。eval()执行的代码可以引用环境中定义的变量。
eval()方法很强大,但也十分危险,特别实在用它执行用户输入数据的情况下,因为这可能导致代码注入!
window对象:ECMAScript虽然没有指出如何直接访问Global对象,但在浏览器中,会将全局对象作为window对象加以实现。所以在全局变量中定义的变量和函数,都成为window对象的属性。
Math对象:可以处理很多计算问题,并且比手工写JavaScript代码效率高。
min()和max()方法: 可以传递多个参数,返回参数中的最大或最小值。
var max = Math.max(3, 54, 32, 16); alert(max); //54 var min = Math.min(3, 54, 32, 16); alert(min); //3
取整(Rounding)方法:
Math.ceil() - 往上取舍 (Math.ceil(25.4); //26)
Math.floor() - 往下取舍 (Math.floor(25.7); //25)
Math.round() - 标准取舍(大于0.5往上,小于往下) (Math.round(25.5); //26)
Math.Random():随机取一个数,0<Math.Random()<1。我们可以通过组合Math.Random()和Math.floor()来生成一个一系列整数随机数,公式如下:
number = Math.floor(Math.random() * total_number_of_choices + first_possible_value)
如你要得到一个1到10的随机数:
var num = Math.floor(Math.random() * 10 + 1);
如果你想要得到从2到10的随机数,因为你现在一个值为2,而2到10之间只有9个可供选择的整数,所以这个时候的表达式为:
var num = Math.floor(Math.random() * 9 + 2);
我们也可以使用一个function来帮我们计算出可选项数和第一个值:
function selectFrom(lowerValue, upperValue) { var choices = upperValue - lowerValue + 1; return Math.floor(Math.random() * choices + lowerValue); } var num = selectFrom(2,10); alert(num); //number between 2 and 10, inclusive
我们还可以把这个函数运用到数组里,使其随机选取一个item。
引用类型总结:
在Javascript里,对象被称作引用类型值,而且可以使用一些built-in引用类型来创建特殊的对象。
- 引用类型与传统面向对象的类相似,但实现方法不同
- Object是一个基本类型,其他所有引用类型都是从Object继承基本的行为
- Array类型
- Date类型
- RegExp类型
- 3个基本包装类型:Boolean,Number,String;由于有这3个基本包装类型,所以这些基本类型值可以被当作对象来访问。
- 2个内置的对象:Global和Math,不需要声明,直接可以用。(Global一般表现为window对象)