JS数据类型及存储方式
一、数据类型
- 七种基本数据类型(值类型):
- 字符串(String) 
- 数字(Number) 
- 布尔值(Boolean) 
- 空值(Null) 
- 未定义(undefined) 
- 符号(Symbol) 
- 任意精度的整数(BigInt) 
- 引用数据类型:
- 对象(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.null和undefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据可以通过第四种方法来判断。(后面第三种方法 通过instanceof无法判断null和undefined也是相同道理)
2.JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object
3. instanceof
语法:
object instanceof constructorobject:某个实例对象
constructor: 某个构造函数
instanceof 运算符用来检测
constructor.prototype是否存在于参数object的原型链上。
一般用来判断引用数据类型的判断,如:Object,Function,Array,Date,RegExp等
注意
null和undefined是空值或未定义,是不存在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);    // 返回 trueinstanceof 的实现原理
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')
}参考链接:
 
                     
                     
                        
                        