数据类型
首先明确ECMAscript中的数据类型分为两种,基本类型(7钟)和引用类型(1种)
基本类型:(又称简单数据类型)7种,未定义类型(Undefined),对空类型(Null),布尔类型(Boolean),数字类型(Number),字符串(String),符号类型(Symbol),BigInt;都是按值访问的,即将一个基本类型的数据赋值给另外一个变量,是将元数据拷贝一份赋值的,两变量之间互不影响。
引用类型:引用类型的值(对象)是应用类型的一个实例,在ECMAScript中,应用类型是一种数据结构,用于将数据和功能绑定在一起。它也常被称为类,虽然这种称呼并不妥当。引用类型有时候也被称为对象定义,因为它描述的是一类对象所具有的属性和方法。 除过对象类型都是引用类型,即保存在内存中的对象,按引用访问,即将一个引用类型的地址赋值给另一个变量,这两个变量的值是相同的相当于指针,都指向同一块内存空间, 操作该变量等于操作该变量指向的内存空间的值,当其中一个变量改变时,原变量也会随之改变。如Object,Function,Date,RegExp,Array, Map,Set........但是Object是引用变量的本质
小可耐们注意啦~~~ 引用类型与基本包装类型的主要区别就是对象的生存期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。这意味着我们不能在运行时为基本包装类型添加属性和方法。
类型对比
基本类型主要是把值存储在栈内存中;基础类型赋值会重新创建一个基础值;它们的值直接存储在变量访问的位置。这是因为这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。
引用类型把值存储在堆内存中,堆内存的地址存储在栈内存中;引用类型赋值只是把堆内存的地址赋值给新变量.也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。
堆和栈有啥区别?
堆:堆为队列优先,先进先出(FIFO),使用二级缓存,生命周期与虚拟机的GC算法有关(并不是引用为空就立即被GC),调用速度相对较低。
栈:栈为先进后出(FILO),使用一级缓存,被调用时通常处于存储空间中,调用后被立即释放。
变量类型判断
1. 使用typeof检测
格式: typeof 变量名
返回值:‘number’,‘string’,‘boolean’,‘undefined’,‘symbol’, ‘object’,只有这六种返回类型,剩下均被检测为‘object’。
注意:而且typeof null === 'object' (JS 设计初的bug)
总结:简单,对基础类型检测性能好;
2. 使用instanceof检测
格式:变量名 instanceof 数据类型
判断参照对象(引用类型)的prototype属性所指向的对象书否在被行测对象的原型链上,比如
class Person{
constructor(name) {
this.name = name;
}
}
let p = new Person("张三");
按照描述就是Person的prototype属性所指向的原型对象是否存在于p的原型链中。
这种判断方式,undefined和null被检测为object
总结:能检测出引用类型;不能检测出基本类型;
3. 使用constructor检测
constructor本来是原型链上的属性,指向构造函数,但是根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法,就去原型链上寻找,因此,实例对象也是能够使用constructor属性的。
除了undefined和null,其他类型的变量均能使用constructor判断出类型,只不过使用constructor是不保险的,因为constructor属性是可以被修改的,会导致检测出的结果不正确。比如
function f() { }
f.prototype = new Array();
let ff = new f();
console.log(ff.constructor==true); //true
使用instanceof和constructor,被判断的array必须是当前页面声明的,比如,一个页面(父页面)有一个框架,框架中引用了一个页面(子页面),在子页面中生命了一个Array,并将其赋值给父页面的一个变量,这是判断该变量,Array == object.constructor;会返回false;
原因:
1、array属于引用型数据,在传递过程中,仅仅是引用地址的传递。
2、instanceof操作符是假定只有一个全局执行环境,如果网页中包含多个框架,就会存在两个以上不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。
为了解决这个问题,可以使用ES5中新增的方法 Array.isArray() 方法,这个方法的目的是最终确定某个值到底是不是数组,而不管它是在哪个全局执行环境中创建的。
总结:基本能检测所有的类型(除了null和undefined);constructor易被修改,不可靠
4.使用Object.prototype.toString.call()检测
ECMA对Object.prototype.toString的解释:
Object.prototype.toString( )
When the toString method is called, the following steps are taken:
1. Get the [[Class]] property of this object. 获取对象的类名(对象类型)
2. Compute a string value by concatenating the three strings “[object “, Result (1), and “]”. 然后将[object 获取的类名] 组合
3. Return Result 返回组合结果
这种方式解决了instanceof存在的跨页面问题,也解决了属性检测方式所存在的问题,nice~~~~
总结:所有类型都能检测;写起来比较繁琐,性能不如 typeof 好;
例子
下面定义了常见的变量类型
let num = 7; let num2 = new Number(7);
let str = "hello"; let str2 = new String("hello");
let boo = true; let boo2 = new Boolean(true);
let arr = [1, 2, 3, 4];
let json = { name: "dingdang", age:20};
let func = function() {console.log("this is function")};
let und = undefined;
let nul = null;
let date = new Date();
let reg = /^[a-zA-Z]{5,20}$/;
let error = new Error();
let map = new Map([ ['name', '张三'], ['title', 'Author']]);
let set = new Set(['red', 2, 'blue', 4]);
运行结果见下图:
~~~~~听大神说写博客可以成为小仙女~~~~~