类型
javascript语言的每一个值都属于某一种数据类型。javasc语言规定了7种语言类型。语言类型广泛用于变量、函数参数、表达式、函数返回值等场合。根据最新的语言标准,这七种语言类型分别是:
-
Undefined;
-
Null;
-
Boolean;
-
String;
-
Number;
-
Symbol;
-
Object。
除了ES6中心加入的Symbol类型,剩下的6种类型都是我们日常开发中的老朋友了,但是,想要回答一些问题的话,我们需要重新认识一下这些老朋友,那么我们就从简单到复杂,梳理一下这些类型。
Undefined、Null
我们的第一个问题,为什么有的编程规范要求要求用void0代替undefined?现在我们就来看一下。 Undefined类型表示未定义,他的类型只有一个值,就是undefined。任何变量在赋值前Undefined类型、值为undefined,一般我们可以用全局变量undefined来表达这个值,或者void运算来把任意一个表达式变成undefined值。 但是呢,仅为javas的代码undefined是一个变量,而非是一个关键字,这是javascr语言工人的设计失误之一,所以,我们为了避免无意中被修改,建议用void0来获取undefined的值。 Undefined跟Null有一定的语义差别,Null表示的是:“定义了但是为空”。以,在实际编程中,我们一般不会吧变量渎职为undefined,这样可以保证所有值为undefined的变量,都是从未赋值的自然状态。 Null类型也只有一个值,就是null,它的语义表示空值,与undefined不同,null是javasc关键字,所以在任何代码中,你都可以放心用null关键字来获取null值。
Boolean
Boolean类型有两个值,true和false,它用于把表示逻辑意义上的真跟假,同样有关键字true和false来表示两个值。这个类型很简单。
String
String用于表示文本数据。String有最大长度2^53-1,在一般开发中都是够用的,但是这个所谓的最大长度并非是你理解的字符数。 因为String的意义并非“字符串”,而是字符串的UTF16编码,我们的字符串作为charAt、charCodeAt、length等方法针对的都是UTF16编码。所以,字符串的最大长度,实际上是受字符串的编码长度影响的。 javascript中的字符串永远都是无法改变的,一旦字符串构造出来,无法用 何方式改变字符串额内容,所以字符串具有值类型的特征。 javascript这个设计继承Java,最新标准中是这样解释的,这样设计是为了“性能和尽可能实现起来简单些”。因为现在中很少用到BMP之外的字符。
Number
我们来说说Number类型。Number类型表示就是我们通常意义所说的“数字”。这个数字对应数学中的有理数,当然,在计算机中,我们有一定的精度限制。 javascript中的Number类型有18437736874454810627(2^64-2^53+3)个值。 javascript中的Number类型基本符合lEEE754-2008规定的双精度浮点数规则,但是javascript为了表达几个额外的语言场景(不让除以0出错,引入了无穷大概念) 几个特殊的情况:
- NaN,占用了9007199254740990,这原本是符合IEEE规则的数字的。
- Infinity,无穷大;
- -Infinity,负无穷大。
另外,值得注意的是,javascript中有+0和-0,在加法类运算中他们没有区别,但是除法的场合需要特别留意区分。“忘记检测除以-0,而得到负无穷大”的情况经常回导致错误,而区分+0和-0的方式,正式检测1/小时Infinity还是-Infinity。
根据双精度浮点数的定义,Number类型中有效的整数范围是-0x1fffffffffffff至0x1fffffffffffff,所以Number无法精确表示此范围外的整数。
同样根据浮点数的定义,非整数的Number类型无法用==(===也不行)来比较,一段著名代码:
console.log( 0.1 + 0.2 == 0.3);
这里输出的结果是false,说明两边不相等的,这是浮点运算额特点,也是很多同学疑惑的来源,浮点数运算的精度问题导致等式左右的结果并不是严格相等,而是相差了个微笑的值。 所以实际上,这里错误的不是结论,而是比较的方法,正确的比较是使用javascript提供最小精度值:
console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);
检查等式左右两边差的绝对值是否小于最小精度,才是正确的比较浮点数的方法。这个结果就是true了。
Symbol
Symbol是ES6中引入的新类型,它是一切非字符串的对象key的集合,在ES6规范中,整个对象系统被用Symbol重塑。
Symbol可以具有字符串类型的描述,但是即使描述相同,Symbol也不相等。
我们创建Symbol的方式是使用全局的Symbol函数。例如:
var mySymbol = Symbol("my symbol");
一些标准中踢到的Symbol,可以在全局的Symbol函数的属性中找到。例如,我们可以使用Symbol.iterator来自定义for...of在对象上的行为:
var o = new Object
o[Symbol.iterator] = function() {
var v = 0
return {
next: function() {
return { value: v++, done: v > 10 }
}
}
};
for(var v of o)
console.log(v); // 0 1 2 3 ... 9
代码中我们定义了iterator之后,用for(var v of o)就可以调用这个函数,然后我们可以根据函数的新歌日,产生一个for...of的行为。
这里我们给对象哦添加了Symbol.iterator属性,并且按照迭代器的要求定义了一个0到10的迭代器,之后我们就可以在for of中愉快的使用这个o对象了。
这些标准中被称为“众所周知”的Symbol,也构成了语言的一类接口形式。他们允许编写与语言结合更紧密的API。
Object
Object是javascript中最复杂的类型,也是javascript的核心机制之一。Object表示对象的意思,它是一切有形和无形物体的总称。
下面我们来看一看,为什么给对象添加的方法能用在基本类型上?
在javascript中,对象的定义是“属性的集合”。属性分为数据属性和访问器属性,二者都是key-value结构,key可以s字符串或者Symbol类型。
事实上,javascript中的“类”仅仅是运行时对象的一个私有属性,而javascript中是无法自定义类型的。
javascript中的几个基本类型,都在对象类型中有一个“亲戚”。它们是:
- Number;
- String;
- Boolean;
- Symbol。
所以我们必须认识到3与new Number(3)是完全不同的值,他们一个是Number类型,一个是对象类型。
Number、String和Boolean,三个构造器是两用的,当跟new搭配时,他们产生对象,当直接调用时,他们表示强制类型转换。
Symbol函数比较特殊,直接用new调用时会抛出错误,但是它依然是Symbol对象的构造器
比如:
console.log("abc".charAt(0)); //a
甚至我们在原型上添加方法,都可以应用于基本类型,比如以下代码,在Symbol原型上添加了hello方法,在任何Symbol类型变量都可以调用。
Symbol.prototype.hello = () => console.log("hello");
var a = Symbol("a");
console.log(typeof a); //symbol,a 并非对象
a.hello(); //hello,有效
类型转换
因为js是弱类型语言,所以类型转换发生非常频繁,我们熟悉的运算都会先进行类型转换,大部分类型转换符合我们的直觉,但是我们不去理解类型转换的严格定义,很容易造成一些代码的判断失误。
其中最为著名的是javascript中的“==”运算,因为试图实现跨类型的比较,它的规则很难让人记住。
StringToNumber
字符串到数字的类型转换,存在一个语法结构,类型转换支持二进制、八进制、十进制、十六进制。
- 30;
- 0b111;
- 0o13;
- 0xFF。
此外,javascript支持的字符串语法还包括正负科学计数法,使用大小写的e来表示:
- 1e3;
- -1e-2。
多数情况下,Number是比parseInt和parseFloat更好的选择。
NumberToString
在较小的范围内,数字带字符串的转换是完全符合十进制的表示。当Number绝对值较大或者较小时,字符串表示则是使用科学计算法表示的。具体的算法,可以去看看javascript的语言标准。
结束语
除了这几种语言类型,还有一些其他的规范类型:
- List和Record:描述函数传参过程。
- Set:用于解释字符串集。
- Completion Record:用于描述一场、跳出语句执行过程。
- Reference:用于描述对象的属性。
- Data Block:用于描述二进制数据。