JavaScript的数据类型主要分为两大类:
1. 基本数据类型:
有如下6种:
boolean、number、string、undefined、null、symbol(es6新增)
- 基本数据类型存储的是保存在变量中的实际的值。
 - 变量是对值的一个具名引用,所以变量本身是没有类型的,只有存储的值有类型。
 
2. 引用类型:
有如下1种 :
对象object,对象又可以分为三个子类型:object、array、function
- 引用类型存储的是保存在一个地址中的值。
 - 包含引用类型值的变量实际上存储的不是值本身,而是一个指向值的指针。
 
var a = [];
var b = a;
b.push(1);
a    //  [1]
b    //  [1]
从上面代码可以看出,复制一个引用类型的值,复制的其实是变量的指针指向,所以两个变量指向的都是同一个对象。
Javascript有三种方法判断一个值属于什么类型:
typeof
instanceof
Object.prototype.toString
1. typeof
1.1 number string boolean
console.log(typeof 10);    //  number 
console.log(typeof 'abc');    //  string
console.log(typeof true);    //  boolean
数值、字符串、布尔值分别返回number、string、boolean
// 字面量式声明变量
var v1 = 123;
var v2 = 'abc';
var v3 = true;
console.log(typeof v1);    //  number
console.log(typeof v2);    //  string
console.log(typeof v3);    //  boolean
// 函数式声明变量
var v1 = new Number(123);
var v2 = new String('abc');
var v3 = new Boolean(true);
console.log(typeof Number);    //  function
console.log(typeof String);    //  function
console.log(typeof Boolean);    //  function
console.log(typeof v1);    //  object
console.log(typeof v2);    //  object
console.log(typeof v3);    //  object
上面代码中,两种变量声明的方式是等价的。
- 函数式声明中返回的值类型是
object - 函数式声明中使用的函数是
JS自带的原生函数,使用原生函数可以把基本数据类型的值包装成对象 - 作为普通函数使用时(不带有
new),可以将任意类型的值,转为原始类型的值,如Number('123'),是把123转换为数值类型。 
'abc'.length    //  3
abc是一个基本类型的字符串,它不是一个对象,是不能调用length属性的。上面的代码中为什么可以调用,是因为JS会调用new String('abc')的方式。自动将abc字符串转换为包装对象实例,在这个对象上调用length属性,调用结束后,这个临时对象就会自动销毁。
var v1 = 'abc';
v1.length    //  3
v1.name = 'emoji';
v1.name    //  undefined
// 等同于
var v1Obj = new String(v1);    //  String { 0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc" }
v1Obj.length    //  3
v1Obj.name = 'emoji';
自动转换生成的包装对象是只读不能修改的,且在创建并调用属性结束就会自动销毁,下一次调用字符串的属性时,实际调用的是一个新生成的对象,而不是上一次调用时生成的那个对象,所以字符串无法添加新属性也无法取到赋值在上一个对象的属性,如果要为字符串添加属性,只能在它的原型对象上定义String.prototype
var v1 = 'abc';
v1.length    //  3
v1.name = 'emoji';
v1.name    //  undefined
包装对象可以使用Object对象提供的原生方法
valueOf方法返回包装对象实例对应的原始类型的值。
new Number(123).valueOf()    //  123
new String('abc').valueOf()    //  "abc"
new Boolean(true).valueOf()    //  true
toString()方法返回对应的字符串形式
new Number(123).toString()    //  "123"
new String('abc').toString()    //  "abc"
new Boolean(true).toString()    //  "true"
1.2 undefined
console.log(typeof undefined);    //  undefined
上面代码中undefined返回undefined
console.log(x);    //  ReferenceError: x is not undefined
console.log(typeof x);    //  undefined
var x;
console.log(x);    //  undefined
console.log(typeof x);    //  undefined
- 直接访问没有声明的变量会报
ReferenceError: x is not undefined的错误 - 变量
x声明了但是没有赋值,JS会默认赋初始值为undefined
不管变量有没有声明,typeof判断都会返回undefined,但是undefined和not defined是不同的: - 
not defined表示:变量未声明 - 
undefined表示:变量声明了但未赋值
我们可以用typeof来检测一个变量是否存在,换句话说就是一个变量有没有被声明: 
// 错误的写法
if (v) {
  // ...
}
// ReferenceError: v is not defined
// 正确的写法
if (typeof v === "undefined") {
  // ...
}
1.3 null
console.log(typeof null);    //  object
null的类型是object,但null是基本数据类型。
《你不知道的Javascript》译者:
原理是这样的,不同的对象在底层都表示为二进制,在Javascript中二进制前三位都为0的话会被判断为Object类型,null的二进制表示全为0,自然前三位也是0,所以执行typeof时会返回object。
null和undefined
null和undefined都可以表示“没有”,含义非常接近。==运算符判断两者相等
null == undefined
不过它们是有区别的null表示空值,即该
Number(null);    // 0
Number(undefined);     // NaN
1.4 object
console.log(typeof [1, 'a', true]);    // object
console.log(typeof {a:10, b:20});    // object
console.log(typeof new Number(10));    // object
数组、对象都返回object
1.4 function
console.log(typeof function(){});    // function
函数返回function
为什么typeof function返回的是function而不是object?
function也是一个对象,但是function对象与普通对象相比,它的内部有一个[[Call]]方法,用来表示这个对象是可调用的,typeof在判断对象时,如果内部实现了[[Call]]方法,就返回function。函数是一个可调用对象。
1.5 object
console.log(typeof [1, 'a', true]);    // object
console.log(typeof {a:10, b:20});    // object
console.log(typeof new Number(10));    // object
数组、对象都返回object
2. instacneof
对于值类型,通过 typeof判断string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/function,你不知道它到底是一个object对象,还是数组,还是new Number等等。
这个时候就需要用到instanceof
console.log(function(){} instanceof Function);    // true
console.log([1, 'a', true] instanceof Array);    // true
console.log({a:10, b:20} instanceof Object);    // true
console.log(new Number(10) instanceof Object);    // true
内部实现:
// L即fn1  R即Fn
function instance_of(L, R) {
   // O为Fn.prototype
   var R = R.prototype; 
   // L为fn1.__proto__
   L = L.__proto__;
   //执行循环
   while (true) {
        // 不通过
        if (L === null)   
            return false; 
        // 判断 L.__proto__ === R.prototype
        if (L === R)
             return true;
        L = L.__proto__;                   
   }
}      
Instanceof的判断原则是:沿着A的__proto__这条线来找,同时沿着B的prototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false。
具体关于原型(链)相关的知识,可以详见另一篇JS基础知识 - 原型及原型链
3. Object.prototype.toString
- 为什么可以使用对象原型上的方法来判断数据类型呢?
对象类型都包含一个内部属性[[class]],这个属性不能直接访问,一般通过Object.prototype.toString来查看,会返回 
Object.prototype.toString.call([1,2,3]);    // [object Array]
基本类型也可以通过这个方法来查看,
涉及包装对象的概念:
基本类型值是没有对象上的属性和方法的,js内部会对基本数据类型进行包装,把基本数据类型使用原生函数封装成一个对象
基本类型值被各自的封装对象自动包装,包装成了一个对象,所以它们但内部属性有[[class]]
toString方法的作用是返回一个对象的字符串形式,默认情况下返回类型字符串。toString是Object.prototype上定义的方法,所有Object的实例对象都继承了这些方法。
var fn = new Object();
fn.toString()     //  "[object Object]"
var obj = {a:1};
obj.toString()     //  "[object Object]"
上面代码,一个对象调用toString方法,会返回字符串[object Object],该字符串说明对象的类型。
字符串[object Object]本身没有太大的用处,但是通过自定义toString方法,可以让对象在自动类型转换时,得到想要的字符串形式。
var fn = new Object();
fn.toString = function () {
  return 'hello';
};
fn + ' ' + 'world'    //  "hello world"
上面代码,当对象用于字符串加法时,会自动调用toString方法。由于自定义了toString方法,所以返回字符串hello world。
数组、字符串、函数、Date对象都分别定义了自定义的toString方法,覆盖了Object.prototype.toString方法。
[1, 2, 3].toString() // "1,2,3"
'123'.toString() // "123"
(function () {
  return 123;
}).toString()
// "function () {
//   return 123;
// }"
(new Date()).toString()
// "Tue May 10 2016 09:11:31 GMT+0800 (CST)"
上面代码中,数组、字符串、函数、Date 对象调用toString方法,并不会返回[object Object],因为它们都自定义了toString方法,覆盖原始方法。
为什么字符串、数字这种基本类型可以直接调用
toString方法?
涉及包装对象的知识,在另一篇中说明
用来判断一个值的类型时:
var obj = {};
obj.toString() // "[object Object]"
上面代码调用空对象的toString方法,结果返回一个字符串object Object,其中第二个Object表示该值的构造函数。这是一个十分有用的判断数据类型的方法。
由于实例对象可能会自定义toString方法,覆盖掉Object.prototype.toString方法,所以为了得到类型字符串,最好直接使用Object.prototype.toString方法。通过函数的call方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型。
Object.prototype.toString.call(value)
上面代码表示对value这个值调Object.prototype.toString方法。
不同数据类型的Object.prototype.toString方法返回值如下。
数值:返回[object Number]。
字符串:返回[object String]。
布尔值:返回[object Boolean]。
undefined:返回[object Undefined]。
null:返回[object Null]。
数组:返回[object Array]。
arguments 对象:返回[object Arguments]。
函数:返回[object Function]。
Error 对象:返回[object Error]。
Date 对象:返回[object Date]。
RegExp 对象:返回[object RegExp]。
其他对象:返回[object Object]。
这就是说,Object.prototype.toString可以看出一个值到底是什么类型。
console.log(Object.prototype.toString.call(undefined));    // [object Undefined]
console.log(Object.prototype.toString.call(0));    // [object Number]
console.log(Object.prototype.toString.call('abc'));    // [object String]
console.log(Object.prototype.toString.call(true));    // [object Boolean]
console.log(Object.prototype.toString.call(function(){}));    // [object Function]
console.log(Object.prototype.toString.call([1, 'a', true]));    // [object Array]
console.log(Object.prototype.toString.call({a:10, b:20}));    // [object Object]
console.log(Object.prototype.toString.call(null));    // [object Null]
console.log(Object.prototype.toString.call(new Number(10)));    // [object Number]