javascript-第十一章-javascript更多主题

JavaScript的更多主题

1. 数据类型转换

JavaScript是一种无类型语言,如果某个类型的值需要用于其他类型的值的环境中,JavaScript就会自动将 这个值转换成所需要的类型。例如,如果一个数字用于布尔值环境中,它就会被转换成布尔值。如果一个对象用于字符串类型中,那么它就会被转换为字符串。
自动数据类型转换:

使用值的环境
字符串 数字 布尔值 对象
未定义的值 “undefined” NaN false Error
null “null” 0 false Error
非空字符串 原内容 非空字符串对应数字或ASCLL码 true String对象
空字符串 原内容 0 false String对象
0 “0” 原值 false Number对象
NaN “NaN” NaN false Number对象
无穷大 “Infinity” 原值 true Number对象
负无穷大 “-Infinity” 原值 true Number对象
其他所有数字 数字对应字符串 原值 true Number对象
true “true” 1 true Boolean对象
false “false” 0 false Boolean对象
对象 toString() valueOf()或toString()或NaN true 原值

1.1 对象到基本数据类型的转换

只要把非空对象用在布尔值环境中,它就会被转换为true。这适用于所有对象,即使是被转换成false的表示原始值的包装对象也不例外。例如:

1
2
3
4
5
6
7
8
Boolean(new Boolean(false))
true
Boolean(new Number(0))
true
Boolean(new String(''))
true
Boolean(new Array())
true

把对象转为数字是通过首先调用该对象的valueOf()方法来完成的。大多数对象继承了Object对象的默认valueOf()方法。它只是返回对象本身。由于默认的valueOf()方法不返回原始值,所有接下来javascript会通过调用对象的toString()方法,再将字符串转为数字来把对象转为数字。

对于数组,数组的toString()方法把数组元素逐一的转换成字符串,用逗号分隔符分隔连接起来,返回连接后的字符串。因此,没有元素的空数组,被转换为空字符串,空字符串被转换为数字0。如果数组只有一个为n的数字元素,则被转换为n本身,如果数组含有多个元素,则被转换为NaN。例如:

1
2
3
4
5
6
7
8
9
10
Number([])
0
Number([1,2])
NaN
Number(['a'])
NaN
Number([1])
1
Number(['1'])
1

运算符“+”和比较运算符(<、<=、>和>=)既能作用于数字,又能作用于字符串。所以当这两个运算符作用于对象时,就不太清楚应将该对象转换为数字还是字符串。在大多数情况下,JavaScript会先尝试调用对象的valueOf ()方法对它进行转换。如果该方法返回了原始值(通常是一个数字),就使用那个值。但是valueOf ()方法通常返回的都是未被转换的对象,在这种情况下,JavaScript 将调用对象的tostring()方法对它进行转换。
对于这种转换规则,只有“+”运算符是例外的当“+”作用于Date对象时,首先调用toString()方法进行转换。存在这一例外的原因是Date对象既有toString又有valueOf方法。当“+”作用于一个Date对象时,你想执行的几乎都是连接操作,但是当用于比较运算符时想执行的几乎都是数字比较,以判断时间大小。

大多数对象没有valueOf()方法,或者没有能够返回有用的结果的valueOf()方法。当将“+”运算符作用于一个对象时,通常进行的是字符串的连接而不是加法运算。当将比较运算符作用于一个对象时,通常进行的则是字符串的比较,而不是数字比较。

1.2 显示类型转换

Number(),String(),Boolean(),Object()可以把它们参数转换为合适的类型。
还有别的技巧可以显式类型转换。
要把一个值转换为字符串,可以把它连接到一个空串上:
var str = x + “”;
要把一个值强制转换为数字,就用它减去0:
var number = x - 0;
要把一个值转换为布尔值,需要连用两次”!”:
var bool = !!x;

1.3 从数字到字符串的转换

将数字转换为字符串:

1
2
var str = String(number);
var str = number + ‘’;

另一种方法是:

1
2
var str = number.toString();
// Number的toString()方法又一个可选的参数,说明了转换的基数,如果省略默认是10,也可以是2-36之间的整数

1.4 从字符串到数字的转换

从字符串到数字的转换,可以显式的使用:

1
2
var num = Number(str);
var num = str - 0;

这种转换只适用于基数10,且允许字符串数字前面有空格,不允许字符串数字后面出现任何非空字符。

1
2
3
4
5
6
7
123’ - 0
123
123 ’ - 0
‘a123’ - 0
NaN
123 a’ - 0
NaN

可以适用parseInt()/parseFloat()函数,这两个函数转换并返回字符串开头的所有数字,并忽略其后的所有非数字后缀。

1
2
3
4
5
6
7
8
9
10
11
parseInt('aq123')
NaN
parseInt('123a')
123
// parseInt()只能解析整数,parseFloat()既能解析整数又能解析浮点数,如果一个字符串以’ox’或’0X’开头,那么parseInt()就将它解释为16进制。
parseFloat('3.14 meters')
3.14
parseInt('3.14 meters')
3
parseInt('0xff')
255

parseInt()第二个参数指定要被解析的基数,是2-36之间的整数。
如果不能将字符串转换为数字,就返回NaN。

2. 使用值和使用引用

在JavaScript中可以使用三种方式来操作数值:
1.可以复制它,把它赋值个一个新的变量
2.把它作为参数传递给一个函数或方法
3.可以把它和其他值比较来看是否相等
操作数值时,有2种根本不同的方式,一种“使用值”一种是“使用引用”。
当使用值来操作数据时,重要的是那个值。在一个赋值语句中,会生成一个实际值的副本,这个副本存储在变量中,对象的属性或数组元素中。副本和原始数据是两个分别存放的,独立的值。当使用值将一个数据传递给函数时,传递的是这个数据的副本。如果函数修改了这个值,改变的只是这个数据的副本,并不会修改原始的数据。

另一种操作数值的方式是使用引用。采用这个方式时,数据的实际副本只有一份,操作的是那个数值的引用。如果操作数据时使用的是引用,那么变量保存的并不是那个值,而是数值的引用,复制的,传递的以及比较的都是值的引用。因此在使用引用的赋值语句中,赋予的是数值的引用而不是值的副本更不是值本身。进行了赋值后,新的变量保存的也是对值的引用,这两个引用具有同等效力,如果其中一个修改了数值,那么原始引用也会改变。当使用引用将值传递给函数时,传递的也是值的引用。使用引用将一个数值和另一个数值比较时,比较的是两个引用,看它们是否引用的是同一个数值的唯一副本。
使用值和使用引用

操作 使用值 使用引用
复制 实际复制的是值,是2个不同的独立的副本 复制的只是对数值的引用,如果通过新的引用修改了值,也会改变原始引用
传递 传递的是一个独立的副本,对他的改变在函数外部没有影响 传递的是值的引用,如果通过函数传递的引用修改了值,这个改变在函数外部可见
比较 比较的两个独立的值,以判断是否相等 比较的是2个引用,以判断它们引用的是否是同一个数值,对两个不同的数值的引用不相等,即使这两个数值的是由相同的字节构成的。

2.1 基本类型和引用类型

基本类型使用值操作,引用类型使用引用来操作。
在JavaScript中,数字和布尔值是基本类型,说它们基本,是因为它们只是由小的、固定数量的字节构成的,这些字节是在JavaScript解释器的低层(基本层)进行操作的。另一方面,对象就是引用类型。作为特殊对象类型的数组和函数因此也是引用类型。由于这些数据类型可以包含任意多个属性或元素,所以它们不像固定大小的基本数值那样易于操作。因为对象和数组的值可能变得非常大,所以使用值来操作这些数据很不合理,这样做可能会产生大量低效率的内存复制和比较。
那么字符串又是什么类型的呢?由于字符串的长度是任意的,所以看起来它好像应该属于引用类型。但事实上在JavaScrip中它们通常被当作基本类型,因为它们并不是对象。
例:使用值进行复制,传递,比较

1
2
3
4
5
6
7
8
9
var n = 1;
var m = n;
function add_to_total(total, x){
total = total + x;
}
add_to_total(n,m);
console.log(n,m); //1 1
if(n == 1) m = 2;
console.log(n,m);//1 2

例:使用引用进行复制,传递,比较

1
2
3
4
5
6
7
8
9
var xmas = new Date(2001,11,25);
var solstice = xmas; //赋值的是值的引用
solstice.setDate(21);
xmas.getDate(); //返回21,而不是原始值25
(xmas == solstice) //true

var xmas1 = new Date(2001,11,25);
var solstice1 = new Date(2001,11,25);
(xmas1 == solstice1) //false

2.2 使用值和使用引用:总结

操作不同类型时所采取的方式。

类型 复制所使用的 传递所引用的 比较所引用的
数字
布尔值
字符串 不可变的值 不可变的
对象 引用 引用 引用

3. 无用存储单元收集

JavaScript·使用无用存储单元来回收那些由字符串、对象、数组和函数占用的而且不再使用的内存。

3.1 标记和清除的无用存储单元

一个标记 和清除的无用存储单元回收器会周期性地遍历javascript环境中的所有变量的列表,并且给这些变量所引用的值做标记。如果被引用的值是对象或数组,那么对象的属性或者数组的元素就会被递归地做上标记。通过递归地遍历所有值的树或者图,无用存储单元收集器就能够找到(并标记)仍旧使用的每个值。那些没有标记的值就是无用的存储单元。当采用标记和清除算法的无用单元收集器给所有正在使用的变量做完了标记之后,它就会开始进行清除。在这个阶段中,它将遍历环境中所有值的列表,同时释放那些没有标记的值。经典的标记和清除无用存储单元收集器每次都进行一次完整的标记和一次完整的清除工作,这在使用无用存储单元收集过程的系统中会大大降低系统的速度。该算法较为复杂的变形使效率相对提高,它们在后台执行收集,并不影响系统的性能。

3.2 采用引用计数的无用存储单元收集

当一个对象被创建,而且它的一个引用被存储在变量中,引用计数就为1。当这个对象的引用被复制,并且存储在另一个变量中时,引用计数就增加到2。当保存这些引用的其中一个变量被某个新值覆盖了时,该象的引用计数就减为1。如果引用计数达到了0,那么就没有对这个对象的引用了。由于没有了对副本的引用,所以在程序中也就不会再有对这个对象的引用。因此, Javascript知道此时销毀对象并且收集与之关联的内存是安全的。