数据类型
数据类型
基本数据(原始值):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]这种格式字符串。
简单类型和复杂类型的区别
内存分配
简单类型的内存分配
简单数据类型是存放到栈里面的,这里面直接开辟一个空间存放的是值.
复杂类型的内存分配
引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中。换句话来说就是,复杂数据类型首先在栈里面存放地址—十六进制的,然后这个地址指向堆里面的数据
值类型在存储时变量中存储的是值本身。引用类型在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型
复制值
简单类型
在通过变量把一个原始值赋值到另一个变量时,原始值会被复制到新变量的位置,两个值完全独立,互不干扰。
let num1 = 5;
let num2 = num1;
复杂类型
在把引用值从一个变量赋给另一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区别在于,这里复制的值实际上是一个指针,它指向存储在堆内存中的对象.操作完成后,两个变量实际上指向同一个对象,因此一个对象上面的变化会在另一个对象上反映出来。
传递参数
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的指针指向新的堆空间。
如果是按引用传递,传入不再是栈内存的指针,而是直接传入了指针指向的堆内存