学习笔记3—JavaScript的数据类型

1 数据类型

ECMAScript有6种简单的数据类型(也成为原始类型):Undefined、Null、Boolean、Number、String和Symbol。Symbol(符号)是ECMAScript6新增的。还有一种复杂数据类型叫Object(对象)。

1.1 typeof操作符

因为ECMAScript的类型是松散的,所以需要一种手段来确定任意变量的数据类型。对一个值使用typeof操作符会返回下列字符串之一:
“undefined”表示值未定义
“boolean”表示值为布尔值
“string”表示值为字符串
“number”表示值为数值
“object”表示值为对象或null
“function”表示值为函数
“symbol”表示值为符号

        let message = "some string"; 
        console.log(typeof message); //"string"
        console.log(typeof(message)); //"string"
        console.log(typeof 95); //"number"

因为typeof是一个操作符而不是一个函数,因此不需要参数,但可以使用参数。注意typeof在某些情况下返回的结果可能让人费解,但技术上讲是正确的。比如调用typeof null返回是"object"。这是因为特殊值null被认为是一个空对象的引用。typeof返回的值是字符串:

        let message;
        console.log(typeof typeof message); //string

严格来讲函数在ECMAScript中被认为是对象,并不代表一种数据类型。可是函数也有自己特殊的属性,因此,就有必要通过typeof操作符来区分函数和其他对象。

1.2 Undefined类型

Undefined类型只有一个值,就是特殊值undefined。当使用var或let声明了变量但没有初始化时,就相当于给变量赋予了undefined值。

        let message;
        console.log(message == undefined); //true

在ECMA-262第3版之前是不存在的。增加这个特殊值目的是为了明确空对象指针null和未初始化变量的区别。
注意包含undefined值的变量和未定义变量是有区别的:

        let message;
        console.log(message); //undefined
        console.log(age); //undefined
        
        console.log(message); //undefined
        console.log(age); //报错

无论是声明还是未声明,typeof返回的都是字符串undefined。我们建议在声明变量的同时进行初始化,这样在typeof返回undefined时,就会知道是未声明而不是声明了但未初始化。undefined是一个假值:

        let message; //声明但未初始化
        //age未声明

        if (message) {
    
    
            //这个块不会执行
        }

        if (!message) {
    
    
            //这个块会执行
        }

        if (age) {
    
    
            //报错
        }

1.3 Null类型

Null类型同样只有一个值,即特殊值null。逻辑上讲,null值表示一个空对象指针,这也是给typeof传一个null会返回"object"的原因:

        let car = null;
        console.log(typeof car);//object

在定义将来要保存对象值的变量时,建议使用null来初始化,不要使用其他值。这样只要检查这个变量的值是不是null就可以知道这个变量是否在后来被重新赋予了一个对象的引用,比如:

if(car != null){
    
    
	//car是一个对象的引用
}

undefined值是null值派生而来的,因此ECMA-262将它们定义为表面上相等,如:

console.log(null == undefined);//true

null是一个假值:

        let car = null;
        if(car){
    
    
            //这个块不会执行
        }

        if(!car){
    
    
            //这个块会执行
        }

1.4 Boolean类型

Boolean类型有两个字面值:true和false,这两个布尔值不同于数值,因此true不等于1,false不等于0。注意布尔值字面量true和false是区分大小写的,因此True和False是有效的标识符而不是布尔值。
虽然布尔值只有两个,但所有其他ECMAScript类型的值都有相应布尔值的等价形式。要将一个其他类型的值转换为布尔值,可以调用特定的Boolean()转型函数:

        let message = "Hello world";
        let messageAsBoolean = Boolean(message);

在这个例子中字符串message会被转换为布尔值并保存在变量messageAsBoolean中,Boolean()转型函数可以在任意类型的数据上调用,而且始终返回一个布尔值。下表总结了不同类型与布尔值之间的转换规则:

数据类型 转换为true的值 转换为false的值
Boolean true false
String 非空字符串 " " 空字符串
Number 非0数值 0、NaN
Object 任意对象 null
Undefined N/A(不存在) undefined

理解以上转换非常重要,因为像if等流程控制语句会自动执行其他类型的值到布尔值的转换,例如:

        let message = "Hello world!";
        if (message){
    
    
            console.log("Value is true");
        }

在这个例子中,console.log会输出字符串Value is true,因为字符串message会被自动转换为等价的布尔值true。

1.5 Number类型

整数也可以用八进制或十六进制字面量表示,对于八进制字面量,第一个数字必须是0,然后是相应的八进制数字。如果字面量中包含的数字超出了应有的范围,就会忽略前缀的0,后面的数字序列会被当成十进制数,如下:

        let octalNum1 = 070; //8进制的56
        let octalNum2 = 079; //无效的8进制,当成79处理
        let octalNum3 = 08; //无效的8进制,当成8处理

要创建16进制字面量,必须让真正的数值前缀0x(区分大小写),然后是16进制数字(0~9以及0~F),十六进制数字中的字母大小写均可

        let hexNum1 = 0xA; //16进制的10
        let hexNum2 = 0x1f; //16进制的31

(1)浮点值
要定义浮点值,数值中必须包含小数点,而且小数点后面必须至少有一个数字。虽然小数点前面不是必须有整数,但推荐加上:

        let floatNum1 = 1.1;
        let floatNum2 = 0.1;
        let floatNum3 = .1;

因为存储浮点值使用的内存空间是存储整数值的两倍,因此ECMAScript总是想方设法把值转换为整数。在小数点后面没有数字的情况下,数值就会变成整数,如果数值本身就是整数,只是小数点后面跟着0,那它也会被转换为整数
科学计数法:ECMAScript中科学计数法的格式要求是一个数值(整数或浮点数)后跟一个大写或小写字母e,再加上一个要乘的10的多少次幂:

let floatNum = 3.125e7;

浮点值的精确度最高可达17位小数,但是算数计算中远不如整数精确,例如0.1+0.2得到的不是0.3,而是0.30000000000000004,由于这种微小的舍入错误,导致很难测试特定的浮点值:

        let a = 0.3,
            b = 0.1;
        if (a + b == 0.3) {
    
    
            console.log("You got 0.3");
        }

(2)值的范围
ECMAScript可以表示的最小数值保存在Number.MIN_VALUE中,这个值在多数浏览器中是5e-324;可以表示的最大数值保存在Number.MAX_VALUE中,这个值在多数浏览器中是1.7976931348623157e+308。如果某个计算得到的值结果超出了JavaScript可以表示的范围,那么这个数值就会被自动转换为一个特殊的Infinity(无穷)值。任何无法表示的负数以-Infinity(负无穷大)表示,任何无法表示的正数以Infinity(正无穷大)表示。
要确定一个值是不是有限大,可以使用isFinite()函数。
(3)NaN
有一个特殊的数值叫NaN,意思是不是数值(Not a Number),用于表示本来要返回数值的操作失败了。比如,用0除任意数值在其他语言中通常都会导致错误,从而中止代码执行。但在ECMAScript中0、+0或-0相除会返回NaN:

        console.log(1 / 0); //Infinity
        console.log(-1 / 0); //-Infinity
        console.log(-0 / 0); //NaN

NaN的独特属性:任何涉及NaN的操作始终返回NaN,其次NaN不等于包含NaN在内的任何值。

        console.log(NaN == NaN); //false;

为此,ECMAScript提供了isNaN()函数。该函数接受一个参数,可以是任意数据类型,然后判断这个参数是否“不是数值”。把一个值传给isNaN()后,该函数会尝试把它转换为数值。某些非数值的值可以直接转换成数值,任何不能转换为数值的值都会导致这个函数返回true

        console.log(isNaN(NaN));//true
        console.log(isNaN(10));//false
        console.log(isNaN("10"));//false
        console.log(isNaN("blue")); //true
        console.log(isNaN(true));//false

(4)数值转换
有3个函数可以将非数值转换为数值:Number()、parseInt()和parseFloat()。Number()是转型函数,可以用于任何数据类型。后两个函数主要用于将字符串转换为数值。对于同样的参数,这三个函数执行的操作也不同。
Number()函数基于如下规则执行转换:
布尔值,true转换为1,false转换为0.
数值,直接返回。
null,返回0.
undefined,返回NaN
字符串,应用以下规则:

  • 如果字符串包含数值字符,包括数值字符前面带加、减号的情况,则转换为一个十进制数值。因此,Number(“1”)返回1,Number(“123”)返回123,Number(“011”)返回11(忽略前面的0);
  • 如果字符串包含有效的浮点值格式如"1.1",则会转换为相应的浮点值(同样会忽略前面的0)。
  • 如果字符串包含有效的十六进制格式如“0xf”,则会转换为与该十六进制对应的十进制整数值。
  • 如果是空字符串则返回0
  • 如果字符串包含除上述情况之外的其他字符,则返回NaN。

对象,调用valueOf()方法,并按照上述规则转换返回的值。如果转换结果是NaN,则调用toString()方法,再按照转换字符串的规则转换。

        let num1 = Number("Hello world"); //NaN
        let num2 = Number(""); //0
        let num3 = Number("000011"); //11
        let num4 = Number(true); //1

parseInt():
考虑到用Number()函数转换字符串时相对复杂而且有点反常规,通常在需要得到整数时可以优先使用parseInt()函数。parseInt()函数更专注于字符串是否包含数值模式。字符串最前面的空格会被忽略,从第一个非空格字符串开始转换。如果第一个字符串不是数值字符、加号或减号,parseInt()立即返回NaN。这意味着空字符串也会返回NaN(这点跟Number()不一样,它返回0)。如果第一个字符是数值字符、加号或减号,则继续依次检测每个字符,直至字符串末尾,或碰到非数值字符。如“1234blue”会被转换为1234,因为blue会被完全忽略。类似地,“22.5”会被转换为22,因为小数点不是有效的整数字符。
parseInt()函数也能识别不同的整数格式(十进制、八进制、十六进制)。如果字符串以"0x"开头,就会被解释为16进制,如果字符串以"0"开头,且紧跟着数值字符,在非严格模式下会被某些实现解释为8进制整数:

        let num1 = parseInt("1234blue");//1234
        let num2 = parseInt("");//NaN
        let num3 = parseInt("0xA"); //10
        let num4 = parseInt(22.5); //22
        let num5 = parseInt("70"); //70
        let num6 = parseInt("0xf");//15

不同的数值格式很容易混淆,因此parseInt()也接受第二个参数,用于指定底数,如果知道要解析的值是16进制,那么可以传入16作为第二个参数,以便正确解析:

let num = parseInt("0xAF",16);

事实上,如果提供了16进制参数,那么字符串前面的0x可以省略。
parseFloat():
parseFloat()函数的工作方式跟parseInt()函数类似,都是从位置0开始检测每个字符。同样,它也是解析到字符串末尾或者解析到一个无效的浮点数值字符为止。这意味着第一次出现的小数点是有效的,但第二次出现的小数点就无效了,此时字符串的剩余字符都会被忽略。
parseFloat()函数的另一个不同之处在于,它始终忽略字符串开头的0.这个函数能识别前面讨论的所有浮点格式,以及十进制格式(开头的0始终被忽略)。16进制数值始终会返回0.因此parseFloat()只解析十进制值,因此不能指定底数。

        let num1 = parseFloat("1234blue");//1234
        let num2 = parseFloat("0xA");//0
        let num3 = parseFloat(22.5); //22.5
        let num4 = parseFloat("22.34.5"); //22.34
        let num5 = parseFloat("0908.5"); //908.5
        let num6 = parseFloat("3.125e7");//31250000

1.6 String类型

字符串可以使用双引号(")、但引号(’)或反引号(`)标识,因此下面的代码都是合法的:

        let firstName = "John";
        let lastName = 'Jacob';
        let lastName = `Jingleheimerschmidt`;

ECMAScript语法中表示字符串引号没有区别。
(1)字符字面量
字符串数据类型包含一些字符字面量,用于表示非打印字符或有其他用途的字符:

字面量 含义
\n 换行
\t 制表
\b 退格
\r 回车
\f 换页
\ 反斜杠()
单引号
" 双引号
` 反引号
\xnn 以十六进制编码nn表示的字符(其中)n是十六进制数字0~F,例如\x41等于"A"
\unnn 以十六进制编码nnnn表示的Unicode字符(其中n是十六进制数字0~F),例如\u03a3等于希腊字母“ Σ \Sigma Σ
        let firstName = "John";
        console.log(firstName.length);

转义序列表示一个字符,字符串的长度可以通过其length属性获取。
(2)字符串的特点
ECMAScript中的字符串是不可变的(immutable),意思是一旦创建,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量。
(3)转换为字符串
有两种方式把一个值转换为字符串。首先是使用几乎所有值都有的toString()方法,这个方法唯一的用途就是返回当前值的字符串等价物:

        let age = 11;
        let ageAsString = age.toString();
        let found = true;
        let foundAsString = found.toString();

toString()方法可见于数值、布尔值、对象和字符串值。null和undefined值没有toString()方法。
如果你不确定一个值是不是null或者undefined,可以使用String()转型函数,它始终会返回表示相应类型值的字符串。

        let value1 = 10;
        let value2 = true;
        let value3 = null;
        let value4;

        console.log(String(value1));
        console.log(String(value2));
        console.log(String(value3));
        console.log(String(value4));

(4)模板字面量
ECMAScript6新增了使用模板字面量定义字符串能力。与使用单引号或双引号不同,模板字面量保留换行字符,可以跨行定义字符串:

        let value1 = 'first line\nsecond line'
        let value2 = `first line
second line`;
        console.log(value1);
        //first line
        //second line

        console.log(value2);
        //first line
        //second line

顾名思义,模板字面量在定义模板时特别有用,比如下面这个HTML模板:

        let pageHTML = `
        <div>
            <a href = "#">
                <span>Jake</span>
            </a>
        </div>
        `;

由于模板字面量会保持反引号内部的空格,因此在使用时要格外注意。格式正确的模板字符串看起来可能缩进不当。
(5)字符串插值
模板字面量最常用的一个特性是支持字符串插值,也就是可以在一个连续定义中插入一个或多个值。技术上讲,模板字面量不是字符串,而是一种特殊的JavaScript句法表达式,只不过求值得到的是字符串。模板字面量在定义时立即求值并转换为字符串实例,任何插入的变量也会从它们最近的作用域中取值。
字符串插值通过在${}中使用一个JavaScript表达式实现:

        let value = 5;
        let exponent = 'second';
        //以前,字符串插值是这样实现的
        let interpolatedString = value + ' to the ' + exponent + ' power is ' + (value * value);

        //现在可以用模板字面量这样实现:
        let interpolatedTemplateLiteral = `${
      
      value} to the ${
      
      exponent} power is ${
      
      value*value}`;

        console.log(interpolatedString);
        console.log(interpolatedTemplateLiteral);

所有插入的值都会使用toString()强制转型为字符串,而且任何JavaScript表达式都可以用于插值。在插值表达式中可以调用函数和方法:

        function capitalize(word) {
    
    
            return `${
      
      word[0].toUpperCase()}${
      
      word.slice(1)}`;
        }
        console.log(`${
      
      capitalize('hello')},${
      
      capitalize('world')}!`);

猜你喜欢

转载自blog.csdn.net/qq_43599049/article/details/112790814