当前位置: 首页 > 科技观察

您对Javascript数据类型了解多少?

时间:2023-03-15 15:52:50 科技观察

众所周知,数据类型是js的入??门知识点,在整个js学习过程中也是非常重要的。数据类型看似简单,但是对于边界数据类型的判断问题和围绕它的深拷贝浅拷贝问题,新手很难理解。1.数据类型JavaScript是弱类型或动态类型,也就是说你不需要事先声明变量的类型,类型会在程序运行过程中自动确定。这意味着您可以使用相同的变量来保存不同类型的数据。js内存分为栈内存(stack)和堆内存(heap)。存储原始类型。堆内存:存储引用类型(在栈内存中存储一??个基本类型值,用来保存对象在堆内存中的地址,用来引用这个对象)。数据类型按存储方式分为两类:基本数据类型(简单数据类型、原始数据类型):值存储在栈内存中,当被引用或复制时,会创建一个完全相等的变量。占用空间小,大小固定,按值访问,是经常使用的数据。引用数据类型(复杂数据类型):地址存放在栈内存中,值存放在堆内存中,多次引用会指向同一个地址。占用空间大,内存占用不固定。如果存放在栈中,会影响程序运行的性能;引用数据类型在堆栈上存储一个指针,该指针指向堆中实体的起始地址。解释器在查找引用值时,首先获取其在栈中的地址,获取到地址后,再从堆中获取实体。数据类型按以上标准划分,常见的有:基本数据类型:String、Number、Boolean、Undefined、Null、Symbol、BigInt复杂数据类型:Object、Array、Date、Function、RegExp等未命名文件(1).png2.数据类型的检测数据类型的检测通常有3种方法:typeofinstanceof2.1typeof使用typeof检测基本数据类型(null除外),但对于引用数据类型,除了function,其他的无法判断。typeof"yichuan";//"string"typeof18;//"number"typeofundefined;//undefinedtypeoftrue;//booleantypeofSymbol();//"symbol"typeofnull;//"object"typeof[];//"object"typeof{};//"object"typeofconsole;//"object"typeofconsole.log;//"function"2.2instanceof使用instanceof遍历原型链,可以准确判断复杂的引用数据类型,但不能准确判断基础数据类型。letFun=Function(){};letfun=newFun();funinstanceofFun;//trueletstr=newString("yichuan");strinstanceofString;//trueletstr="yichuan";strinstanceofString;//false2.3Object.prototype.toString.call()Object.prototype.toString方法返回对象的类型字符串,因此可以用来判断一个值的类型。因为实例对象可能会自定义toString方法,重写Object.prototype.toString,所以最好在使用的时候加上call。使用此方法可以非常准确地检测所有数据类型。Object.prototype.toString.call("yichuan");//["objectString"]Object.prototype.toString.call(18);//["objectNumber"]Object.prototype.toString.call(true);//["objectBoolean"]Object.prototype.toString.call(null);//["objectNull"]Object.prototype.toString.call(newSymbol());//["objectSymbol"]Object.prototype.toString.call({});//["objectObject"]Object.prototype.toString.call([]);//["objectArray"]Object.prototype.toString.call(/123/g);//["objectRegExp"]Object.prototype.toString.call(function(){});//["objectFunction"]Object.prototype.toString.call(newDate());//["objectDate"]Object.prototype.toString.call(document);//["objectHTMLDocument"]Object.prototype.toString.call(window);//["objectWindow"]我们可以看到这样输出的结果都是["objectXxxx"]首字母大写。2.4一般数据类型判断方法functiongetType(obj){//先判断输入数据判断返回结果是否为objectif(typeofobj!=="object"){returntypeofobj;}//对于typeof返回对象,再进行具体判断,用正则返回结果,记住正则returnObject.prototype.toString.call(obj).replace(/^\[object(\S+)\]$/,"$1中间有个空格");}记住:使用typeof返回的类型是小写的toString返回的类型是大写的getType("yichuna");//"string"getType(18);//"number"getType(true);//“布尔”getType(未定义);//"undefined"getType();//"undefined"getType(null);//"Null"getType({});//"Object"getType([]);//"Array"getType(function(){});//"Function"getType(newDate());//"Date"getType(/123/g);//"RegExp"3.数据类型转换3.1强制类型转换常见的强制类型转换方法有分别是:Number()String()Boolean()parseInt()parseFloat()toString()3.2Number()方法的强制转换规则布尔值true和false分别转换为1和0,数字返回自身null返回0undefined返回NaNString如果字符串只包含数字,将其转换为十进制如果字符串只包含有效浮点格式,转为浮点值如果是空字符串,转为0如果不是上述格式的字符串,则返回NaNSymbol抛出异常3.3Boolean()方法转换规则undefined、null、false、""、0(含+0、-0)、NaN转换时均为false,其他类型转换均为true。特别注意:boolean({})转为true3.4隐式类型转换==如果类型相同,不需要类型转换如果其中一个运算值是null或undefined,那么另一个运算符必须是null或undefined才能返回true,否则两者都返回false如果其中一个值是Symbol类型,则返回false如果其中一个运算为Boolean,则将其转为number如果两个运算值都是string和number,则将字符串转为数字如果一个运算值是object,另一个是string、number或symbol,则将object转为原来的数据类型判断小测试:null==undefined;//truenull==0;//false""==null;//false""==0;//true会转成number类型再判断"123"==123;//true0==false;//true1==true;//true3.5隐式类型转换+“+”运算符,不仅可以做数字相加,还可以做字符串拼接。如果其中一个是字符串,另一个是数字、undefined、null、boolean,则调用toString()方法进行字符串拼接。如果是纯字符串、数组、正则表达式等,默认调用对象的转换方法会优先,然后拼接。如果加上string和bigInt,bigInt会先转换成string。如果number类型加上undefined,NaN1+2;//31+"2";//"12""1"+undefined;//"1undefined""1"+null;//"1null""1"+true;//"1true""1"+1n;//"11"string和bigInt相加,会先把bigInt转成string1+undefined;//NaNundefined会先转成NaN1+null;//1null会被转换为01+true;//21+1n;//如果部署Error3.6对象转换规则先调用Symbol.toPrimitive方法,然后返回调用valueOf()。如果转换为基本类型,则返回toString()。如果转换为基本数据类型,则返回。如果没有返回基本数据类型,就会报错。4、Deepcopy和Shallowcopy在js编程中经常需要拷贝数据,那么什么时候用deepcopy,什么时候用shallowcopy,是开发过程中需要考虑的事情吗?如何提高手写js的能力,以及如何处理一些边界条件下的深度思考能力呢?有两个重要的问题:如何复制很多嵌套的对象?深文案能写到什么程度才能让面试官满意?4.1浅拷贝的原理及实现自己创建一个新的对象,来接受一个对象值进行重新拷贝或引用。如果对象属性是基本数据类型,则将基本数据类型的值复制到新对象;如果对象属性是引用数据类型,则分配内存中的地址。如果其中一个对象改变了内存中的地址,一定会影响到另一个对象4.1.1Object.assignObject.assign是es6中object的一个方法,可以用于合并js对象等多种用途,其中一个就是执行浅拷贝。Object.assign(target,...sources);//target目标对象,要复制的sources对象注意:Object.assign不会复制对象的继承属性Object.assign不会复制对象的不可枚举属性object例如:letobj={};letobj1={name:"yichuan",scores:{math:100,Chinese:100}};Object.assign(obj,obj1);console.log(obj);//{name:"yichuan",scores:{math:100,Chinese:100}}改变目标对象的值:我们可以看到改变下面的目标对象的值会导致被复制的对象的值改变.letobj={};letobj1={name:"yichuan",scores:{math:100,Chinese:100}};Object.assign(obj,obj1);console.log(obj);//{name:"yichuan",scores:{math:100,Chinese:90}}obj.scores.Chinese=10;console.log(obj);//{name:"yichuan",scores:{math:100,Chinese:90}}console.log(obj1);//{name:"yichuan",scores:{math:100,Chinese:90}}不可复制不可枚举属性letobj1={user:{name:"yichuan",age:18},idCard:Symbol(1)};Object.defineProperty(obj1,"innumerable",{value:"Non-enumerableproperty",enumerable:false});letobj2={};Object.assign(obj2,obj1);obj1.user.name="onechuan";console.log("obj1",obj1);//{user:{...},idCard:Symbol(1),innumerable:'不可枚举属性'}console.log("obj2",obj2);//{user:{...},idCard:Symbol(1)}可以看出没有无数个属性4.1.2展开运算符/*对象的副本*/letobj1={user:{name:"yichuan",age:18},school:"实验小学"};letobj2={...obj1};obj2.school="五道口男子技术学校";console.log(obj1);//{学校:》实验小学“,user:{name:'yichuan',age:18}}obj2.user.age=19;console.log(obj2);//{school:"实验小学",user:{name:'yichuan',age:19}}/*数组复制*/letarr=["red","green","blue"];letnewArr=[...arr];console.log(arr);//['red','green','blue']console.log(newArr);//['red','green','blue']4.1.3concat复制数组数组的concat方法实际上是浅复制letarr=["red","green","blue"];letnewArr=arr.concat();newArr[1]="black";console.log(arr);//["red","green","蓝色的”];console.log(newArr);//["red","black","blue"];4.1.4slice复制数组slice方法只针对数组类型,arr.slice(begin,end);letarr=["red","green","blue"];letnewArr=arr.slice();newArr[1]="black";console.log(arr);//["red","green","blue"];console.log(newArr);//["红","黑","蓝"];4.1.5手写浅拷贝基本数据类型最基本的拷贝为引用数据类型新建存储,并拷贝一层对象属性函数shallowClone(target){//先判断是否是对象数据类型if(typeoftarget==="object"&&target!==null){//判断输入是对象类型还是数组类型constcloneTarget=Array.isArray(target)?[]:{};//遍历目标对象元素for(letpropintarget){//判断cloneTarget对象上是否有该属性,不复制if(!cloneTarget.hasOwnProperty(prop)){cloneTarget[prop]=target[prop]}}returncloneTarget;}returntarget;}4.2深拷贝原理及实现我们知道,浅拷贝只是创建一个新的对象,将原对象的基本类型的值进行复制。对于复杂的引用数据类型,它在堆内存中完全开辟了一块内存地址,将原来的对象完整复制存储。深拷贝是将内存中的一个对象完全复制到目标对象中,并在堆内存中开辟新的空间来存放新对象的值,新对象值的变化不会影响原来的对象目的,即实现患者的二次隔离。4.2.1JSON.stringify()其实在实际开发过程中使用的最简单的深拷贝就是使用JSON.stringify()和JSON.parse()。但其实是有缺陷的,不影响易用性。注:letobj1={user:{name:"yichuan",age:18},school:"实验小学"};letobj2=JSON.parse(JSON.stringify(obj1));console.log(obj1);//{school:"实验小学",user:{name:'yichuan',age:18}}console.log(obj2);//{school:"实验小学",user:{name:'yichuan',age:18}}obj2.school="门头沟学生";obj2.user.age=19;console.log(obj1);//{school:"实验小学",user:{name:'yichuan',age:18}}console.log(obj2);//{school:"MentougouCollege",user:{name:'yichuan',age:19}}4.2.2简单的手写深拷贝作为简单的手写深拷贝,只能完成基础拷贝功能也有一些缺陷:不能复制非可枚举属性和符号类型只能递归复制普通引用类型的值在对象的属性中形成循环,即循环引用没有得到妥善解决functiondeepClone(obj){constcloneObj={};//遍历对象键名for(letkeyinobj){//判断是否是对象类型if(typeofobj[key]==="object"){//如果是对象,调用函数再次递归复制cloneObj[key]=deepClone(obj[key]);}else{//如果是基本数据类型,直接复制值cloneObj[key]=obj[key];}}returncloneObj;}constobj1={user:{name:"yichuan",age:18},school:"实验小学"}letobj2=deepClone(obj1);obj1.user.age=19;console.log(obj2);//{学校:"ExperimentalElementarySchool",user:{name:'yichuan',age:18}}4.2.3手写深拷贝优化版对于上面那种简单版的深拷贝,很明显面试官不买账,所以我们针对递归进行升级处理,对于不可枚举的属性和可以遍历的Symbol类型,我们可以使用Reflect.ownKeys方法,当参数为Date和RegExp类型时,直接生成新的实例并返回。使用Object的getOwnPropertyDescriptors方法可以获取对象的所有属性,相应的特征,顺便结合Object.create()方法创建一个新的对象,并继承原对象的原型链。使用将WeakMap类型作为Hash表,因为WeakMap是弱引用类型,可以有效防止内存泄漏,作为检测循环引用帮助很大。如果有循环,则引用直接返回WeakMap中存储的值hash=newWeakMap()){//判断是否为日期类型if(obj.constructor===Date)returnnewDate(obj);//判断是否为正则对象if(obj.constructor===RegExp)returnnewRegExp(obj);//如果是Circularreference,使用weakMap解决传入参数constcloneObj=Object.create(Object.getPrototypeOf(obj),allDesc);//继承原型链hash.set(obj,cloneObj);for(constkeyofReflect.ownKeys(obj)){cloneObj[key]=isComplexDataType(obj[key])&&typeofobj[key]!=='function'?deepClone(obj[key]):obj[key];}returncloneObj;}constobj1={num:2021,str:'jue',bool:true,nul:null,arr:['ref','green','blue'],date:newDate(0),reg:newRegExp('/123/g'),user:{name:'yichuan',age:18},school:'实验小学'};constobj2=deepClone(obj1);obj1.user.age=19;console.log(obj2);//{arr:['ref','green','blue'],bool:true,date:ThuJan01197008:00:00GMT+0800(中国标准准确时间),nul:null,num:2021,reg:/\/123\/g/,school:"实验小学",str:"jue",user:{name:'yichuan',age:18}}5参考学习《如何写出一个惊艳面试官的深拷贝?》《JavaScript基本数据类型和引用数据类型》《Javascript核心原理精讲》6写在最后其实在实际的开发和使用过程中,很多人对深拷贝的细节并没有深入的了解。如果你能深入研究细节,你会发现这部分内容对于理解更深层次的js底层原理有很大帮助。这篇文章是对数据类型、数据类型检测、数据类型强制和隐藏转换、深浅拷贝的简单总结。希望对大家有所帮助。