JavaScript中的原始类型(primitive type)包括Undefined、Null、Number、Boolean和String,其余变量均为引用类型,也就是Object Type。原始类型保存位置是“栈内存”,而引用类型保存在“堆内存”中,但一般JavaScript中对变量的使用,并不十分关心变量在内存中的位置。编程
“typeof”操做符用以获取变量的值的数据类型。typeof能够接受变量名或字面量值做为操做数,返回一个描述变量类型信息的字符串。须要注意的是,typeof的返回值与JavaScript中的类型并非一一对应的:浏览器
“undefined” ——变量值未定义安全
“number” ——变量值是数值编程语言
“boolean” ——变量值是布尔值函数
“string” ——变量值是字符串this
“object” ——变量值是对象或者null编码
“function” ——变量值是函数spa
另外,typeof是一个像(+,-)同样的操做符,而不是函数,虽然形如“typeof(12)”的用法不会产生错误,但对于操做符来讲“typeof 12”才是合适的使用方法。指针
一、undefined和nullcode
Undefined Type在ECMA-262文档中的定义是:
The Undefined type has exactly one value, called undefined. Any variable that has not been assigned a value has the value undefined.
Undefined类型只有一个惟一的值“undefined”,变量的值为undefined意味着变量没有被初始化。对于还没有使用var声明的变量,使用它会产生错误,但使用typeof操做符会返回“undefined”:
var foo; alert(foo); // undefined alert(bar); // 错误 alert(typeof foo); // undefined alert(typeof bar); // undefined
undefined被实现为一个全局变量(而不是像null同样的字面值),它的值是“未定义”。在ECMAScript 3中,undefined能够被赋予其它值,在ECMAScript 5中已被修正为只读的。
Null Type 也只有一个值null,用来表示“空值”。多数编程语言中的都有相似null、nil等用来表示空值的字面量。但与其余编程语言不一样的是,JavaScript并不使用null表示未初始化的变量的值(由undefined表示)。null的逻辑意义是表示一个空对象指针。JavaScript中一般意义的对象并不包括简单数据类型,因此逻辑上null表示变量指向了一个空值的Object类型(不是字面量“{}”)。出于这个缘由,即可理解为何使用typeof操做符获取null值的类型会获得“object”了。JavaScript里null值对Object类型的意义,相似于0对Number类型,“”对于String类型。
undefined和null都用来描述“空值”,但在逻辑意义上,undefined比null要更为“底层”一些。通常状况下,不须要显示的把变量值指定为undefined。而对于一个意在保存Object但尚未真正指向一个对象的变量,则应该把变量值设置为null,体现null做为空对象指针的做用而且与undefined区分开来。
二、数值
ECMAScript使用了简化的数字模型。它只有一个数字类型Number,而没有分离出单独的整数类型。在实现上,Number类型采用了IEEE 754标准定义的64位双精度浮点数格式。64位的浮点数格式中,52位用来表示尾数,11位表示指数,1位符号。所以在表示整数时,JavaScript可以表示的整数范围在-Math.pow(2,53)和Math.pow(2,53)之间,超过这个范围,低位数字的精度便没法保证了。
var n = Math.pow(2,53); // 9007199254740992 alert(n === n + 1); // true, 9007199254740992 + 1获得的值仍是9007199254740992
在实际的Web开发中,若须要在后台(如Java)传递一个Long Int类型给Javascript处理,极可能JavaScript把JSON数据解析为Number类型后,获得的结果并非你想要的:它的后面几位数字发生了变化。
JavaScript使用浮点数值进行运算,所以小数部分会出现精度问题,这跟全部其余采用IEEE 754标准格式表示浮点数的编程语言同样。避免在代码中出现对小数部分的相等判断。(整数部分是精确的)
var a = 0.1; var b = 0.2; alert(a + b === 0.3); // false
若是数值超过了JavaScript所能表示数字上限(overflow),将被自动转换为一个表明无穷大的Infinity(或-Infinity,负无穷)值;若是数值无限接近0而且超过JavaScript表示范围(underflow),将被设置为0(或-0,同0)。JavaScript不会出现溢出错误(包括被零整除的时候)。
var a = Number.MAX_VALUE * 2; //Infinity var b = Number.MIN_VALUE / 2; //0 var c = 1 / 0; //Infinity or NaN, 依JS执行环境不一样 var d = 0 / 0; // NaN
Number类型定义了一个特殊的值NaN,即not-a-number。NaN的意义表明一个本该获得数值的地方没有获得任何数值。任何使用NaN作操做数的算术运算,都会获得NaN。另外NaN也是惟一一个使用对自身进行相等判断会获得false的数值。NaN的这个怪异之处破坏了JavaScript运算符的对称性。若是在代码中须要经过比较数值大小进行分支判断,就须要注意可能出现NaN的状况,由于使用NaN与其余数值进行大小比较总会获得false,而这可能不是你想要的结果。
var a = 10; a = a - "not number" // NaN alert(a === a); // false var b = 12 + a; // NaN var c = 10; alert(b >= c || b < c); // false
另外一个Number类型中不常引人注目的地方是位运算。JavaScript中提供了按位操做运算符。在不少其余编程语言中,按位操做能够进行硬件级处理,因此很是快。可是JavaScript没有整数类型,它的位操做是先把数值转换为32位整数,而后进行计算,而后再转换回去,JavaScript绝大部分运行环境是在浏览器中,与硬件相隔较远,所以位操做执行很慢。
三、字符串
与不少其余编程语言中同样,JavaScript中的字符串值也是不可变的,改变一个字符串变量的值,至关于从新生成了一个字符串并把它赋值给变量。JavaScript中的简单类型没法进行方法调用(做为this调用函数),但咱们仍是能够进行诸如
"abcdefg".toUpperCase();
这样的操做。这是由于JavaScript为简单数据类型提供了一种方式,把它们包装为对象类型,而后进行方法调用。”"abcdefg"“先被隐式地包装为对象,而后使用包装出的对象调用toUpperCase方法,待方法调用结束后,JavaScript再隐式地把包装对象回收。其它简单数据类型也使用一样的方式。也可使用JavaScript提供的构造函数显示地建立包装对象,JavaScript提供了String()、Number()和Boolean()三个构造函数,分别用于构建String、Number和Boolean类型的包装对象。
四、类型转换
ECMA-262中对数据类型的转换有详细的定义,不少JavaScript的参考资料也会列出类型转换的详细规则,这里就再也不赘述了,下面只讨论一些值得注意的问题。
JavaScript有两组相等比较运算符:”===“和”!==“、”==“和”!=“。Crockford在著做《JavaScript:The Good Parts》里面列举的Bad Parts中的第一个就是”==“运算符。缘由在于”==“运算符会执行隐式的类型转换,而JavaScript中类型转换的规则又很是复杂,很容易致使代码中出现不易发现的bug。与”===“和其余编程语言中的”==“不一样,JavaScript中的”==“运算符并不具有传递性: ∀x∀y∀z(x == y ∧ y == z → x == z)并不成立:
"" == "0"; // false "" == 0; // true "0" == 0; // true
Crockford和Zakas都建议不要使用“==”运算符,而使用“===”代替。若不遵循这个建议,使用“==”运算符时,请务必确保你明确知道两个比较变量的类型信息,以避免发生预料以外的类型转换。
另一个常常用到类型转换的地方是分支判断。if(和其它)语句并不要求进行分支判断的表达式结果必须为Boolean类型,而会根据特定的规则把判断表达式的结果转换为true或false后再进行判断。
if (obj !== undefined && obj !== null) { // code } // 上面的判断条件能够替换为 if (obj) { // code }
上面代码中的obj表明一个对象变量,若其值为undefined或null,则被转换为false,反之转换为true。这种方式并不彻底安全,若使用的变量是简单数据类型,就须要注意一些特殊值的转换规则,不然代码可能不会按照预想的方式执行。
if (typeof num === "number" && num) { // if num is 0, get false //code }
上面代码的本意是获取一个有效的数值类型,屏蔽了其余类型和num的值为NaN的状况(NaN会转换false)。但代码中有一个纰漏,它忽略了num值为0的状况。0值会被转换为false,从而致使下面的代码不会被执行,这可能与编码者的本意相违背。一样使用String类型做为分支条件,也要考虑""会被自动转换为false的状况。
与分支判断中的类型转换类似的状况,是采用短路方式为变量赋值。因为JavaScript中”&&“和”||“操做符的特性,咱们常常采用短路方式为变量赋值。”&&“操做符会返回表达式中第一个能够转换为false的操做数或最后一个操做数(全为true时);”||“操做符返回表达式中第一个能够转换为true的操做数或最后一个操做数(全为false时)。
var obj = obj1 || obj2 || {}; var attr = obj && pro && attr;
与分支判断同样,须要警戒表达式中可能出现的特殊值:0,"",null等。
JavaScript的类型模型,提供了极大的灵活性的同时也带来了不少陷阱,编码过程当中须要当心地规避这些陷阱,由于代码审查很容易忽略它们,出现问题时,它们也每每很难被发现。