JavaScript的7中数据类型和类型转换

254 阅读9分钟

JavaScript的7中数据类型

1.Undefined

  1. Undefinde类型表示未定义,他的类型只有一个值就是undefinde,如:let a; console.log(a) //undefined;
  2. 任何变量在赋值前都是Undefinde类型,值为undefined;
  3. 在JavaScript中undefined是一个变量,并非是一个关键字,这个是JavaScript语言公认的设计失误之一,一般我们可以用全局变量名为undefined来表达这个值,为了避免无意中篡改undefined这个变量,建议使用viod 0 来获取undefined
  4. sessionStorm里如果存储的是undefined,这里的undefined是个字符串
  5. let a; if(a === undefined) 和 if(a === viod 0) 这里的两个表达式都是true
  6. viod () 里的任何表达式都是等于undefined

2.Null

  1. Null类型表示定义了,为空.如:let a = null,if(a) 这里表达式是false,Null类型也只有一个值,就是null;
  2. 与undefined不同的是null是关键字,在任何JavaScript代码中,都可以放心使用null关键字取null值;

3.Boolean

1.Bollean类型有两个值,true 和fase ,它用于表示逻辑意义上的真和假,两个值都是关键字

4.String

  1. String用于表示文本数据,String 有最大长度2^53 - 1;
  2. 一般这个长度是够用,但是这里的最大长度并不是我们理解中的字符数;
  3. String 的意义并非"字符串",而是字符串UTF16编码,所以字符串最大长度,实际上是受字符串编码长度影响;
  4. 我们字符串操作charAt/charCodeAt/length 等方法针对的都是UTF16编码;
  5. UTF是Unicode的编码方式,规定了码点在计算机中的表达方法,常见的有UTF16/UTF8;
  6. Unicode的码点通常用U+??? 来表示,其中???是十六进制的码值点,0-65536(U+0000 - U+FFFF)的码点称为基本字符区域(BMP);
  7. JavaScript中的字符串是永远无法变更的,一旦字符串构造出来,无法用任何方式改变字符串内容,所以字符串有值类型的特征;
  8. JavaScript字符串把每个UTF16单元当做一个字符来处理,所以超出BMP(U+0000 - U+FFFF)的字符串要小心处理;
  9. JavaScript这个设计继承与Java,最新标准中是这样解释的,这样设计是为了"性能和尽可能实现起来简单",因为现实中很少用到BMP之外的字符

5.Number

  1. Number类型表示我们通常意义上的"数字",这个数字大致对应数学中的有理数;
  2. JavaScript中的Number类型有18437736874454810627(即2^64-2^53+3)个值;
  3. JavaScript中的Number类型基本符合IEEE754-2008规定的双精点数的规则;
  4. JavaScript为了表达几个额外的语言场景(比如不让除以0出错,而引入了无穷大的概念,Infinity无穷大,-Infinity 负无穷大),NaN占用了900719925440990,这原本是符合IEE规则的数字;
  5. JavaScript区分+0和-0,在加减法中他们没有区别,但是除法需要留意区分,任何事除以-0都是负无穷大,检测+0和-0的方法是1/x判断是Infinity还是-Infinity;
  6. 根据双精度浮点数的定义,Number类型中的有效整数范围是-0xfffffffffffff-0xfffffffffffff,所以Number无法精确表示此范围外的整数;
  7. 根据浮点数的定义,非整数的Number类型无法用==(===也不行)来比较,一段著名的代码,在JavaScript中0.1 + 0.2 不等于 0.3,就是因为浮点数运算的精度导致有个微小的差值,正确的比较方法是会用JavaScript提供的最小精度值来比较,console.log(Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON;)

6.Symbol

  1. Symbol是ES6中引入的新类型,他是一切非字符串对象key的集合,在ES6规范中,整个系统对象被Symbol重塑; 2.Symbol可以具有字符串类型的描述,但是即使描述相同Symbol也不相等; 3.创造Symbol的方式是使用全局的Symbol函数,例如;let mySymbol = Symbol('my symbol'); 4.一些标准中提到的Symbol可以在全局的Symbol函数的属性中找到,例如,我们可以使用Symbol.iterator来自定义for...of在对象上的行为,代码如下:
var o = new Object  
o[Symbol.iterator] = function() {  
    var v = 0  
    return {  
       nextfunction() {  
             return { value: v++, done: v > 10 }  
       }  
     }          
 };  
 for(var v of o)   
 console.log(v); // 0 1 2 3 ... 9

5.这些标准中称为"众所周知"的Symbol也构成了语言的一类接口形式,他们允许编写语言结合更紧密的API

7.Object

  1. Object 是 JavaScript中最复杂的类型,也是JavaScript和核心机制之一,Object表示对象的意思,他是一切有形和无形的总称.
  2. 在JavaScript中,对象的定义是"属性的集合",属性分为数据属性和浏览器属性,二者都是key-value结构,key可以是字符串也可以是Symbol类型
  3. 因为C++ 和 Java的成功,在这两门语言中,每个类都是一个类型,二者几乎等同,以至于很多人常常会把JavaScript的"类"与类型混淆;
  4. 事实上JavaScript中类仅仅是运行时对象的一个私有属性,而JavaScript是无法自定义类型的
  5. JavaScript中几个基本类型在对象中都有一个亲戚,他们是String/Number/Boolean/Symbol,所以3和new Number(3) 是两个类型,一个数Number类型另一个是对象类型;
  6. String/Number/Bollean三个构造器都是两用的,当根new搭配时,他们产生的是对象,直接使用表示强制类型转换;
  7. Symbol函数比较特殊,直接用new 或报错,但它仍然是Symbol对象构造器.
  8. JavaScript语言设计上试图模糊对象和基本类型的关系,我们日常代码也可以把对象放在基本类型上使用,如:console.log('abc'.charAt(0))//a,甚至我们在原型上添加方法都可以用于基本类型,如下代码:
Symbol.prototype.hello = () => console.log("hello");  
    var a = Symbol("a");  
    console.log(typeof a); //symbol,a并非对象  
    a.hello(); //hello,有效

是因为.运算符提供了装箱操作,他会根据基础类型构造一个临时对象,使我们能在基本类型上调用对象的方法,因为会产生临时对象,所以如果频繁的装箱操作会影响性能.

JavaScript类型转换

因为JavaScript是弱类型语言所以类型转换发生的非常频繁,大部分我们熟悉的运算都会先进行转换,大部分类型转换都符合人类的直觉,如果我们不去理解类型转换的严格定义,很容易造成一些代码中的判断失误.最臭名昭著的是JavaScript中的"=="运算,因为试图实现跨类型比较,他的规则复杂到几乎没有人可以记住.现在我们大多要求程序员使用"==="作对比.

1.StringToNumber

  1. new Number() 支持转换十进制/二进制/八进制,除此之外还支持科学计数法,可以使用大写或者小写的e来表示;
  2. parseInt和parseFloat并不使用这个转换,语法不同,在不传入第二个参数的情况下,parseInt只支持16进制前缀'0x',而且会忽略非数字字符,也不支持科学计数法.在一些古老的浏览器中,parseInt还支持0开头的数字作为8进制前缀,这是很多错误来源,所以在任何环境下,都建议传入parseInt的第二个参数,而parseFloat直接把原字符串作为十进制来解析,它不会引入任何的其他类型.
  3. 多数情况下Number是比parseInt和parseFloat更好的选择;

2.NumberToString

在较小范围内,数字到字符串的转换是完全符合我们直觉的十进制表示,当Number绝对值较大或者较小时,字符串表示则是使用科学计数法表示的,这样其实就是保证了字符串不会过长.

3.装箱转换

  1. 每一种基本类型Number/String/Boolean/Symbol 在对象中都有对应的类,所谓装箱就是把基本类型转换成相对应的对象,他是转换中一种相当重要的类型.
  2. 使用内置的Object函数,可以直接将变量转换成对象类型,代码如下:
    var symbolObject = Object(Symbol("a"));  
    console.log(typeof symbolObject); //object  
    console.log(symbolObject instanceof Symbol); //true  
    console.log(symbolObject.constructor == Symbol); //true

3.每一类装箱对象皆有私有的Class属性,这些属性都可以用Object.prototype.toString获取,代码如下:

var symbolObject = Object(Symbol("a")); 
console.log(Object.prototype.toString.call(symbolObject)); //[object Symbol]

4.在JavaScript中,没有任何方法可以更改私有的Class属性,因此Object.prototype.toString是可以准确识别对象对应的基本类型的方法,它比instanceof(比较方法,如 == >= 等)更准确,需要注意的是,call本身会产生装箱操作,所以需要配置typeof来区分类型还是对象;

4.拆箱操作

  1. 在JavaScript标准中,规定了ToPrimitive函数,他是对象类型转换基本类型;
  2. 对象到String和Number的转换都遵循,先拆箱再转换的规则,通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的String或者Number,拆箱转换都会先尝试调用valueOf和toString来获得拆箱后的基本类型,如果valueOf和toString都不存在,或者没有返回基本类型,则会产生类型错误TypeError,代码如下:
var o = { 
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}} }
o * 2 // valueOf // toString // TypeError

3.我们定义了一个对象o,o有valueOf和toString两个方法,这两个方法都返回一个对象,然后我们进行o*2这个运算的时候,可以看见先执行了valueOf,接下来是toString,最后抛出了一个TypeError这就说明了这个拆箱转换失败了. 4.上述代码如果执行的是String(o)就会先执行toString方法,再执行valueOf然后抛出TypeError这说明拆箱失败了. 5.在ES6之后,还允许对象通过显示指定@@toPrimitive Symbol来覆盖原有的行为.代码如下:


    var o = {
        valueOf : () => {console.log("valueOf"); return {}},
        toString : () => {console.log("toString"); return {}}
    }

    o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}


    console.log(o + "")
    // toPrimitive
    // hello

总结:在本篇文章中,我们介绍了JavaScript运行时的类型系统,除了这七种语言类型,还有一些我们更关心的规范类型,

List 和 Record: 用于描述函数传参过程。<br Set:主要用于解释字符集等。<br Completion Record:用于描述异常、跳出等语句执行过程。
Reference:用于描述对象属性访问、delete 等。
Property Descriptor:用于描述对象的属性。
Lexical Environment 和 Environment Record:用于描述变量和作用域。
Data Block:用于描述二进制数据。
有一个说法是:程序 = 算法 + 数据结构,运行时类型包含了所有JavaScript执行时所需要的数据结构的定义,所以我们要对它格外重试.

补充阅读

事实上,"类型"在JavaScript中是一个有争议的概念,一方面,标准中规定了运行时数据类型;另一方面,JavaScript语言中提供了typeof这样的算法,用来返回操作的类型,但是typeof的运算结果,与运行时类型的规定很多不一致的地方,具体看下表

image.png 在表格中,多数项是对应的,但是请注意object-Null和function-Object是特例,我们在理解类型时候需要特别注意这个区别.