4.1 JavaScript数据类型概述
ECMAScript中有5种简单的数据类型(也称为基本数据类型):Undefined、Null、Boolean、Number和String,还有一种复杂的数据类型:Object。由于ECMAScript数据类型具有动态性,所以不支持任何创建自定义类型的机制,而所有值最终都将是上述6种数据类型之一。
4.1.1 JavaScript数据类型的特点
JavaScript是弱类型语言,对于数据类型的规范比较松散。具体表现如下:
分类简单,且不明确细分。
声明变量时,不用指定数据类型。
使用不严格,可根据需要自动转换数据类型。
数据类型检查比较简单,也比较混乱。
优点:使用限制少,应用灵活,入门门槛比较低。
缺点:开发复杂的程序存在瓶颈,执行效率与强类型语言相比较低。
4.1.2 JavaScript的基本数据类型
JavaScript定义了6种基本数据类型,如表4-1所示。
表4-1 JavaScript定义的6种基本数据类型
【示例】使用typeof运算符可以检测数据的基本类型。下面代码使用typeof运算符分别检测常用直接量的值的类型。
alert(typeof 1); //返回字符串"number" alert(typeof"1"); //返回字符串"string" alert(typeof true); //返回字符串"boolean" alert(typeof{}); //返回字符串"object" alert(typeof[]); //返回字符串"object" alert(typeof function(){}); //返回字符串"function" alert(typeof null); //返回字符串"object" alert(typeof undefined); //返回字符串"undefined"
注意:typeof运算符以字符串的形式返回上述6种基本类型之一。但是,JavaScript把null归为object数据类型,而function(){}归为function类型。把函数视为一种基本数据类型,而不是object的一种特殊类型。
提示:在JavaScript中,函数是一个比较复杂、特殊的数据结构,它可以是函数类型,又可以是对象类型,也可以是类(构造函数、构造器),用法比较灵活,用户应该在具体环境中灵活把握。
4.1.3 值类型和引用类型
4.1.2节从形态角度对数据进行归类,下面从用法角度对其进行概括。任何数据都可以分为两大类:值类型和引用类型。
值类型:也称为原始数据、原始值。值类型是简单的数据,值可以直接赋值给变量,并存储在变量中。
引用型:也称为复合型数据。引用型数据不会直接传递给变量,变量与值之间相互分离,它们之间是引用关系。
在JavaScript中,Number、String、Boolean和Undefined型数据都是值类型,而object、Function和Array等都是引用型数据。
提示:其他编程语言都把字符串视为引用型数据,而不是值类型,因为字符串的长度是可变的。
但是JavaScript把字符串作为值类型进行处理,不过字符串在复制和传递运算中,是以引用型数据的方法来处理的。
【拓展1】值和类型是两个不同的概念。例如,null是Null类型的唯一值,undefined是Undefined类型的唯一值,而true和false是Boolean类型仅有的两个值等。
在任何语言中,值的操作都可以归纳为以下3个方面。
复制值:把值赋予给变量,或者通过变量把值赋值给另一个变量、属性或数组元素。
传递值:把值作为参数传递给函数或方法。
比较值:通过逻辑运算符,把值与另一个值进行比较,看是否相等。
由于值类型数据和引用型数据的值存在形式不同,它们的操作方法和运算结果也是不同的。
1.使用原始值
对于原始值来说,其操作的3个层面说明如下。
(1)复制值
在赋值语句中,操作的过程将会产生一个值的副本,副本与实际值之间没有任何联系。
【示例1】在本示例中,分别把值123复制3份给变量a、数组b和对象c,虽然它们的值是相等的,但它们之间是相互独立的。
var n = 123, a, b = [], c = {}; a=n; //复制数字123 b[0]=n; //复制数字123 c.x=n; //复制数字123 (a == b[0]) && (a == c.x) && (b[0] == c.x) && alert("复制的值都是相等的"); //检测它们的值都是相等的
(2)传递值
当把值传递给函数或方法时,传递的值仅是副本,而不是值本身。
【示例2】当在函数中修改传递进来的值时,结果只能够影响这个参数值,并不会影响到原来的值。
var a=123; //原来的值 function f(x){ x = x + x; } f(a); //调用函数修改传递的值 alert(a); //查看变量a的值是否受影响,返回值为123,说明没有变化
(3)比较值
当对原始值进行比较时,比较的是值本身,而不是值所处的位置,比较结果可能会相等,这只能说明它们所包含的字节信息是相同的。
2.使用引用值
对于引用值来说,其操作的3个层面说明如下。
(1)复制值
在赋值语句中,所赋的值是对原值的引用,而不是原值副本。赋值之后,变量保存的是对原值的引用。当在多个变量、数组元素或对象属性中间复制时,它们都会与原始值保持引用关系。
【示例3】所有引用都具有相同的功能,通过编辑其中的引用变量的值,这种修改将会在原值及其他相关引用中体现出来。
var a=[1,2,3]; //赋值数组引用 b=a; //复制值 b[0]=4; //修改变量b中第一个元素的值 alert(a[0]); //返回4,显示变量a中第一个元素的值也被修改为4
但是,如果给变量b重新赋予新值,则新值不会影响原值内容。
var a=[1,2,3]; //赋值数组引用 b=a; //复制值 b=4; //为变量b重新赋值 alert(a[0]); //变量a的内容保持不变
重复赋值实际上是覆盖变量对原值的引用,变为另一个值的副本或对其引用,所以不会对原值产生影响。
(2)传递值
当使用引用将数据传递给函数时,传递给函数的也是对原值的一个引用,函数可以使用这个引用来修改原值本身,任何修改在函数外部都是可见的。
【示例4】在下面代码中,把数组a作为参数传递给函数f,则在函数体内可以修改函数体外数组的元素值。
var a = [1,2,3]; function f(x){ x[0]=4; //在函数中修改参数值 } f(a); //传递引用值 alert(a[0]); //返回4,原值发生变化
在函数内可以使用外部引用的值,但是如果在函数内部使用一个新的值覆盖原来的引用,那么在函数内部的修改就不会影响原来引用的值。
(3)比较值
当比较两个引用值时,比较的是两个引用地址,看它们引用的原值是否为同一个副本,而不是比较它们的原值字节是否相等。
【示例5】在下面代码中,当对两个不同值进行引用时,尽管它们由相同的字节构成,但是这两个引用的值却是不相等的。
var a=new Number(1); //引用值a var b=new Number(1); //引用值b var c=a; //把a的引用赋值给c alert(a==b); //返回false alert(a==c); //返回true
【拓展2】使用值和使用引用都是数据操作的两种基本方法。在操作数据时,要采用什么方法进行处理,主要看数据的类型。值类型和引用型数据参与运算的方式不同,值类型数据通过使用值来操作数据,而引用型数据使用引用来操作数据。
【示例6】在下面代码中,值类型数据是以实际值参与函数内部运算,因此与原值没有直接关系,而引用型数据是以引用地址参与运算,计算的结果会影响到引用地址所关联的原值。
var s="abc"; //字符串,值类型数据 var o=new String(s); //字符串对象,被装箱后的字符串 function f(v){ //运算函数 v.toString=function(){ //修改参数的方法toString() return 123; }; } f(s); //传入值 alert(s); //返回字符串"abc",说明运算没有对原数据造成影响 f(o); //传入引用 alert(o); //返回数值123,说明运算已经影响到原数据的内部结构