【js进阶3】数据类型

JavaScript | 2020-07-23 18:27:06 736次 6次

一、栈(stack)和堆(heap)

  stack 为自动分配的内存空间,它由系统自动释放;而 heap 则是动态分配的内存,大小也不一定会自动释放


二、数据类型

  JS 分两种数据类型:

    基本数据类型:Number、String、Boolean、Null、 Undefined、Symbol(ES6),这些类型可以直接操作保存在变量中的实际值,存放在栈中

    引用数据类型:Object(在JS中除了基本数据类型以外的都是对象,数组是对象,函数是对象,正则表达式是对象)。引用类型是存放在堆内存中的对象,变量其实是保存的在栈内存中的一个指针(保存的是堆内存中的引用地址),这个指针指向堆内存。通过这个引用地址可以快速查找到保存中堆内存中的对象


三、数据类型判断

这个看似很简单的一个问题,放在 js 中彷佛没有那么简单了,大致可以通过四种方式来判断,各有优缺点,某些情况下还需要相互结合使用。

1.typeof

先看下它的底层表示,javascript 的最初版本中,使用的 32 位系统,为了性能考虑使用低位存储了变量的类型信息:

    000:对象

    1:整数

    010:浮点数

    100:字符串

    110:布尔

    undefined:用 - (−2^30)表示

    null:对应机器码的 NULL 指针,一般是全零,所以 typeof null = 'object'

基本数据类型判断:

typeof "";  //string
typeof 1;   //number
typeof false; //boolean
typeof undefined; //undefined
typeof Symbol(); //symbol

引用数据类型判断:

typeof function(){}; //function
typeof {}; //object
typeof null; //object
typeof []; //object
typeof new Date(); //object
typeof new RegExp(); //object

使用 typeof 对基本数据类型判断没问题,引用数据类型判断就不灵了。


2.instanceof

它是用来判断 left 是否 right 的实例,具体使用可以查看上一篇中的原型和继承,看下这个方法的模拟实现:

function instance(left,right){
    let prototype = right.prototype;
    let proto = Object.getPrototypeOf(left); //__proto__
    while(true){
        //按照上篇文章中那张图,到终点了指向null 或者找不到原型
       if (proto === null || proto === undefined){
           return false;
       }
       if (proto === prototype){
           return true;
       }
       //沿着链 继续往下找
       proto = Object.getPrototypeOf(proto);
    }
}
console.log(instance({},Object)); //true
console.log(instance([],Number)); //false

针对基本数据类型判断通过这个方法是无效的。

引用数据类型判断:

({}) instanceof Object; //true
[] instanceof Array;  //true
[] instanceof Object; //true
(()=>{}) instanceof Function; // true
(()=>{}) instanceof Object; // true
new String(123) instanceof String; //true  通过new 构造的  类型是object

可见通过这种方式判断数据类型是不安全的。


3.constructor

它可以让对象诞生以后,就具有可追溯的数据类型,参照上篇的原型图,构造函数的 prototype 上有一个 constructor 属性,并让其指向构造函数。

'1'.constructor == String // true
new String('1').constructor == String //true
new String('1').constructor == Object //false

(1).constructor == Number // true
new Number(1).constructor == Number //true
new Number(1).constructor == Object // false

true.constructor == Bollen // true

[].constructor == Array //true
[].constructor == Object//false

(()=>{}).constructor == Function //true
(()=>{}).constructor == Object   //false

constructor 判断无法判断 null undefined,通过 new String('1') 这种方式判断也是不准确的。


4.Object.prototype.toString()

toString() Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]]。这是一个内部属性,其格式为 [object Xxx],其中 Xxx 就是对象的类型。但是对于其他对象,则需要通过 call、apply 来调用才能返回正确的类型信息。

Object.prototype.toString.call([])
"[object Array]"

Object.prototype.toString.call('')
"[object String]"

采用这个方法可以完美的判断所有数据类型(NaN 除外,这个方法判断会返回 Number,使用 isNaN 判断,isNaN() 不传值或者值为 NaN 时返回 true),比如可以封装为一个函数:

let isString = obj => Object.prototype.toString.call( obj ) === '[object String]';

isString('') //true 
isString(11) // false

但是这么多类型一个个创建函数太麻烦,可以通过高阶函数方式封装一个抽象的方法:

let isType = type => obj => {
  return Object.prototype.toString.call( obj ) === '[object ' + type + ']';
}
isType('String')('123');	// true
isType('Array')([1, 2, 3]);	// true
isType('Number')(123);		// true

6人赞

分享到: