JavaScript 网页编程从入门到精通 (清华社"视频大讲堂"大系·网络开发视频大讲堂)
上QQ阅读APP看书,第一时间看更新

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,说明运算已经影响到原数据的内部结构