JS基础知识 - 数据类型

JavaScript的数据类型主要分为两大类:
1. 基本数据类型:

有如下6种:
booleannumberstringundefinednullsymboles6新增)

  • 基本数据类型存储的是保存在变量中的实际的值。
  • 变量是对值的一个具名引用,所以变量本身是没有类型的,只有存储的值有类型。
2. 引用类型:

有如下1种 :
对象object,对象又可以分为三个子类型:objectarrayfunction

  • 引用类型存储的是保存在一个地址中的值。
  • 包含引用类型值的变量实际上存储的不是值本身,而是一个指向值的指针。
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

数值、字符串、布尔值分别返回numberstringboolean

// 字面量式声明变量
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,但是undefinednot 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

nullundefined都可以表示“没有”,含义非常接近。==运算符判断两者相等

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__这条线来找,同时沿着Bprototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false
具体关于原型(链)相关的知识,可以详见另一篇JS基础知识 - 原型及原型链

3. Object.prototype.toString
  • 为什么可以使用对象原型上的方法来判断数据类型呢?
    对象类型都包含一个内部属性[[class]],这个属性不能直接访问,一般通过Object.prototype.toString来查看,会返回
Object.prototype.toString.call([1,2,3]);    // [object Array]

基本类型也可以通过这个方法来查看,
涉及包装对象的概念:
基本类型值是没有对象上的属性和方法的,js内部会对基本数据类型进行包装,把基本数据类型使用原生函数封装成一个对象
基本类型值被各自的封装对象自动包装,包装成了一个对象,所以它们但内部属性有[[class]]
toString方法的作用是返回一个对象的字符串形式,默认情况下返回类型字符串。toStringObject.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]

你可能感兴趣的:(JS基础知识 - 数据类型)