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。

this对象:this是函数执行时所处的作用域。

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对象)
posted @ 2012-07-13 15:07  Rex.M  阅读(2425)  评论(0编辑  收藏  举报