JS数据类型及存储方式


JS数据类型及存储方式

一、数据类型

  1. 七种基本数据类型(值类型):
  • 字符串(String)

  • 数字(Number)

  • 布尔值(Boolean)

  • 空值(Null)

  • 未定义(undefined)

  • 符号(Symbol)

  • 任意精度的整数(BigInt)

  1. 引用数据类型:
  • 对象(Object)

  • 数组(Array)

  • 函数(Function)

二、存储方式——栈和堆

基本数据类型的内存分配:直接将数据存储在栈空间
引用数据类型的内存分配:将数据地址存储在栈空间,真正的数据存储在堆空间中。
堆内存是无序存储,可以根据引用直接获取。

内存分配

①原始数据类型:
栈内存 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小比较稳定,属于被频繁使用数据,所以放入栈中存储;

②引用数据类型:
堆内存 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定, 而在栈内存中只是存了一个地址来表示对堆内存中的引用。

三、简单类型与复杂类型传参

  • 简单类型传参:函数形参可看作一个变量,当把值类型变量传给形参,是把栈空间中的值复制一份传过去,形参与实参操作的不是同一个对象
let a = 1;
let b = a;
console.log(a);  // 1
console.log(b)   // 1

b = 2;      // !! 这里修改b,只会修改b的值,而a的值不受影响
console.log(a);  // 1
console.log(b)   // 2
  • 复杂类型:函数形参可看作一个变量,当把引用类型变量传给形参,是把栈空间中的地址传过去,形参与实参保存同一个地址,操作同一个对象
let a = { name: '张三' };
let b = a;
console.log(a);  // { name: '张三' }
console.log(b)   // { name: '张三' }

b.name = '李四';  // !! 这里修改b,会修改b指向的实际对象,即与a指向的同一个的对象
console.log(a);  // { name: '李四' }
console.log(b)   // { name: '李四' }

四、数据类型判断方法

1. typeof

使用typeof进行判断时, 返回结果一般只包括这8种:
number,boolean,string,function,object,undefined,symbol(ECMAScript 2015 新增),bigint(ECMAScript 2020新增)

注意!! 没有null

判断除了null的基本数据类型时,可以使用typeof关键字, 示例如下:

typeof '123'     //  string
typeof 1        //  number
typeof true     //  boolean
typeof Symbol('1')   // symbol
typeof 111n    //  bigint
typeof undefined        // undefined
typeof null     // object
typeof {a:1,b:2}    // object
function c() {console.log('123')}
typeof c     //  function

官方文档更多更详细的示例: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/typeof#示例

这里解释一下为何用typeof判断null时,得到的结果为何是对象object:
这是因为在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。然而null的机器码全都是0,所以就被当成了对象看待.

2. constructor

constructor是prototype对象上的属性,指向构造函数。

根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的。

示例

var num  = 123;
var str  = 'abcdef';
var bool = true;
var arr  = [1, 2, 3];
var json = {name:'wenzi', age:25};
var func = function() { console.log('this is function'); }
var und  = undefined;
var nul  = null;
var date = new Date();
var reg  = /^[a-zA-Z]{5,20}$/;
var error= new Error();

function Person(){

}
var tom = new Person();

// !! undefined和null没有constructor属性 !!
console.log(
    tom.constructor==Person,
    num.constructor==Number,
    str.constructor==String,
    bool.constructor==Boolean,
    arr.constructor==Array,
    json.constructor==Object,
    func.constructor==Function,
    date.constructor==Date,
    reg.constructor==RegExp,
    error.constructor==Error
);
//以上所有结果均为true

注意

1.nullundefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据可以通过第四种方法来判断。(后面第三种方法 通过instanceof无法判断nullundefined也是相同道理)
2.JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object

3. instanceof

语法: object instanceof constructor

object:某个实例对象

constructor: 某个构造函数

instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。

一般用来判断引用数据类型的判断,如:Object,Function,Array,Date,RegExp等

注意nullundefined是空值或未定义,是不存在prototype属性,没有原型链的,也就无法使用instanceof来判断了

console.log(
    'advsz' instanceof String, //false
    123 instanceof Number, //false
    false instanceof Boolean, //false
    [1,2,3,4] instanceof Array, //true
    { a:1,b:2,c:3 } instanceof Object, //true
    function(){console.log('aaa');} instanceof Function, //true
    undefined instanceof Object, //false
    null instanceof Object, //false
    new Date() instanceof Date, //true
    /^\[object (\S+)\]$/g instanceof RegExp, //true
    new Error() instanceof Error //true
)

instanceof 也可以判断一个实例是否是其父类型或者祖先类型的实例。

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const BWM = function() {}
BWM.prototype = new Car();

let mycar = new BWM();
console.log(mycar instanceof BWM);    // 返回 true
console.log(mycar instanceof Car);    // 返回 true
console.log(mycar instanceof Object);    // 返回 true

instanceof 的实现原理

instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。

因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。

大致代码原理如下:

function new_instance_of(leftVaule, rightVaule) {
    let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
    leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
    while (true) {
    	if (leftVaule === null) {
            return false;
        }
        if (leftVaule === rightProto) {
            return true;
        }
        leftVaule = leftVaule.__proto__
    }
}

4. 通用及最准确的方法——Object.prototype.toString.call()方法

var toString = Object.prototype.toString;
console.log(toString.call(123)); //[object Number]
console.log(toString.call('123')); //[object String]
console.log(toString.call(true)); //[object Boolean]
console.log(toString.call({})); //[object Object]
console.log(toString.call([])); //[object Array]
console.log(toString.call(function(){})); //[object Function]
toString.call(new Date); // [object Date]
toString.call(Math); // [object Math]

//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]

5. 以上1,4两种方法的结合版,如果不是typeof能判断的基本数据类型,则使用进行判断,代码示例如下:

/**
 * 获取数据类型
 * @param {any} target
 * @return {String}
 */
const getType = (target) => {
  const type = typeof target;

  if (type != 'object') {
    return type;
  }
  return Object.prototype.toString.call(target).replace(/^\[object (\S+)\]$/g,'$1')
}

参考链接:

时清云 判断js数据类型的四种方法和原理

hahazexia 判断数据类型的方法


文章作者: hjwforever
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hjwforever !
评论
  目录