数据类型

67 阅读4分钟

数据类型

数据类型

基本数据(原始值):number、bigint、string、Boolean、null、undefined、Symbol

引用数据类型:对象Object包括普通对象Object、数组对象-Array、正则对象-RegExp、日期对象-Date、数学函数-Math、函数对象-Function

保存原始值的变量是按值访问的,保存引用值的变量是按引用访问的。

数据类型检测

typeof

console.log(typeof('hello')) // string
console.log(typeof(1))   // number
console.log(typeof(true))  // boolean
console.log(typeof(undefined))  // undefined
console.log(typeof(null))   // object
console.log(typeof({}))  // object
console.log(typeof([]))   // object
console.log(typeof(() => {}))  // function
console.log(typeof(Symbol()))  // symbol

缺点:不能将Object,Array和Null都区分开,都返回object

原理:js在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息,

000: object,表示这个数据是一个对象的引用。

1: int,表示这个数据是一个 31 位的有符号整型。

010: double,表示这个数据是一个双精度浮点数的引用。

100: string,表示这个数据是一个字符串的引用。

110: boolean,表示这个数据是一个布尔值。

这里对于 undefined 和 null 来说,这两个值的信息存储是有点特殊的

null:所有机器码均为0

undefined:用 −2^30 整数来表示

instanceof

语法:object instanceof constructor

object:某个实例对象

constructor:某个构造函数

console.log('hello' instanceof String) // 非对象实例,因此返回 false
console.log(1 instanceof Number)   // false
console.log(true instanceof Boolean)  // false
console.log({} instanceof Object)  // true
console.log([] instanceof Array)   // true
console.log(function(){} instanceof Function)  // true
console.log({} instanceof Object)  // true
// 手写instanceof
function myInstanceof(left, right) {
    let leftVal = Object.getPrototypeOf(left)
    const rightVal = right.prototype
    while(leftVal !== null) {
        if (leftVal === rightVal)
            return true
        leftVal = Object.getPrototypeOf(leftVal)
    }
    return false
}
function Car() {}
const myCar = new Car()
console.log(myInstanceof(myCar, Car))

优点:能够区分Array、Object、Function,适合用于判断自定义类的实例对象

缺点:Number、Boolean、String基本数据类型不能判断

原理:检测 constructor.prototype 是否存在于参数 object 的原型链上。

Object.prototype.constructor

console.log((9).constructor === Number);  //true
console.log('hello'.constructor === String);  //true
console.log(true.constructor === Boolean);  //true
console.log((function(){}).constructor === Function); //true
console.log((new Date).constructor === Date);  //true
console.log({}.constructor === Object);  //true
console.log([1, 2, 3].constructor === Array);  //true

原理:constructor属性返回返回对创建此对象的数组函数的引用

缺点:null 和 undefined 是无效的对象,因此是不会有 constructor 存在的

Object.prototype.toString

console.log(Object.prototype.toString.call("jerry"));//[object String]
console.log(Object.prototype.toString.call(12));//[object Number]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(undefined));//[object Undefined]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call([]));//[object Array]

原理:toString方法被调用时:

获取this指向的那个对象的[[Class]]属性的值。(这也是我们为什么要用call改变this指向的原因)

计算出三个字符串"[object "、 第一步的操作结果Result(1)、 以及 "]" 连接后的新字符串。

返回第二步的操作结果Result(2),也就是类似 [object className]这种格式字符串。

简单类型和复杂类型的区别

内存分配

简单类型的内存分配

简单数据类型是存放到栈里面的,这里面直接开辟一个空间存放的是值.

image-20220809202528565.png

复杂类型的内存分配

引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中。换句话来说就是,复杂数据类型首先在栈里面存放地址—十六进制的,然后这个地址指向堆里面的数据

image-20220809202548188.png 值类型在存储时变量中存储的是值本身。引用类型在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型

复制值

简单类型

在通过变量把一个原始值赋值到另一个变量时,原始值会被复制到新变量的位置,两个值完全独立,互不干扰。

let num1 = 5;
let num2 = num1;

image-20220809202653850.png

复杂类型

在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象.操作完成后,两个变量实际上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来。

image-20220809202709324.png

传递参数

ECMAScript中所有函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另一个变量一样。

在按值传递参数时,值会被复制到一个局部变量。如果把整个对象作为参数传递,那么传递的值就是这个对象的引用。

function setName(obj) {
  obj.name = 'Nicholas';
  obj = new Object();//函数执行完毕后,就销毁掉了
  obj.name = 'Greg';
  //console.log(obj);//{name:Greg}
}
let testObj = new Object();
setName(testObj); 
console.log(testObj.name);  // Nicholas

obj相当于是拷贝了一份testObj栈内存中的指针,重新创建对象并赋值给obj的时候,obj的指针指向新的堆空间。

如果是按引用传递,传入不再是栈内存的指针,而是直接传入了指针指向的堆内存