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 constructor
object:某个实例对象
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); // 返回 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')
}
参考链接: