JavaScript | 数据类型,常量以及变量
这应该是JavaScript系列教程第三期,这期准备讲一下数据类型,常量以及变量的定义。
任何的编程语言都有自己的数据类型与数据结构,那么JavaScipt也有自己的数据类型,可以将其分为基本数据类型与引用数据类型!基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构成的对象。当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值。
在JavaScipt 中,基本类型(基本数值、基本数据类型)是一种既非对象也无方法或属性的数据。有 7 种原始数据类型:
基本数据类型是保存在栈中的,存取的速度比较快。
引用数据类型我们通常统称为Object类型,大致上分为一下5种:
引用数据类型是保存在堆当中的,这样说可能不准确,准确的说法应该是引用数据类型的对象保存在了堆当中。
将堆和栈进行一下对比:
我们定义一个基本数据类型在栈中,应该是以下的情况。
上面的栗子就是定义了
const a = 1const b = 2const c = 3const d = 4
以上看起来是挺简单的一对一的形式储存的。
我们同样来定义一下引用数据类型:
const e = new Object()const f = ee.name = 'orange'
以上代码的存储形式应该如下:
解释一下上面图片,左边是一个栈,定义了一个引用数据类型,名称叫做e,然后e在堆中开辟了一个空间,有了一个地址,所以栈中的值就是这个空间的地址,然后又定义了一个引用数据类型,叫做f,把e赋值给f,这个时候值也是指向了这个空间的地址。最后给这个空间加了一个值,name:'orange'。最后就是e和f其实是一个东西了。
如果你没有听清楚,没搞明白,我来给你简单翻译一下!
假如你有一个空地,这个空地就是栈,然后这个空地上面来了a,b,c,d,e,f这几个人(也就是名称),然后呢a.b,c,d这四个人都是本地人,就在这个空地上修了个房间(也就是值),分别放了1,2,3,4。但是呢,e,f他不是本地人,人家有自己房子,跑到你的地方来了,他也修了东西,但是只是一个门,这个地址(也就是值)是他家(也就是堆),刚好f和e是一家人,所以就用了e的门,也通向了这个家(堆)。
不知道以上的解释清楚了没,在不清楚的话,私信我,我去做个动画给你描述清楚!!!!
在代码中,我们是怎么判断数据类型的,数据类型怎么转化呢?
我们有很多方法可以用来判断。
typeof 基本只能判断出基本数据类型,引用类型除了function外,都是 object 。
const typeOf = () => { // 基本类型 console.log(typeof(1)); //number console.log(typeof('1')); //string console.log(typeof(null)); //object console.log(typeof(false)); //boolean console.log(typeof(undefined)); //undefined console.log(typeof(Symbol())); //symbol console.log(typeof(NaN)); //number // 引用类型 console.log(typeof {}); //object console.log(typeof []); //object console.log(typeof Error('1')); //object function A() {} console.log(typeof A); //function}typeOf()
instanceof 返回是true或false,只能对引用数据类型进行判断。
const instanceOf = () => { // 基本类型 console.log(1 instanceof Number); //false console.log("1" instanceof String); //false console.log(true instanceof Boolean); //false console.log(null instanceof Object); //false console.log(undefined instanceof Object); //false console.log(Symbol() instanceof Symbol); //false // 引用类型 console.log({} instanceof Object); //true console.log([] instanceof Array); //true console.log(Error('1') instanceof Error); //true function A() {} console.log(A instanceof Function); //true}instanceOf()
Object.prototype.toString.call() 返回是一个[object xxx]的字符串,可以通过slice(8, -1)的方式来获取完整的数据类型。
const stringCall = () => { // 基本类型 console.log(Object.prototype.toString.call(1)); //[object Number] console.log(Object.prototype.toString.call(1).slice(8, -1)) //Number console.log(Object.prototype.toString.call('1').slice(8, -1)) //String console.log(Object.prototype.toString.call(true).slice(8, -1)) //Boolean console.log(Object.prototype.toString.call(null).slice(8, -1)) //Null console.log(Object.prototype.toString.call(undefined).slice(8, -1)) //Undefined console.log(Object.prototype.toString.call(NaN).slice(8, -1)) //Number console.log(Object.prototype.toString.call(Symbol()).slice(8, -1)) //Symbol // 引用数据类型 console.log(Object.prototype.toString.call({}).slice(8, -1)) //Object console.log(Object.prototype.toString.call([]).slice(8, -1)) //Array console.log(Object.prototype.toString.call(Error('1')).slice(8, -1)) //Error function A() {} console.log(Object.prototype.toString.call(A).slice(8, -1)) //Function}stringCall()
即可以判断基本数据类型,也可以判断引用数据类型,由于null和undefined没有constructor,所以判断不了,同时由于constructor可以改变,所以此方法如果改写了constructor,就不准确了。
const constructorName = () => { // 基本数据类型 console.log((1).constructor.name); //Number console.log('1'.constructor.name); //String // console.log((null).constructor.name); //报错 Cannot read properties of null (reading 'constructor') // console.log((undefined).constructor.name); //报错 Cannot read properties of undefined (reading 'constructor') console.log((true).constructor.name); //Boolean console.log((NaN).constructor.name); //Number console.log((Symbol()).constructor.name); //Symbol // 引用数据类型 console.log(({}).constructor.name); //Object console.log(([]).constructor.name); //Array console.log((Error('1')).constructor.name); //Error function A() {} console.log((A).constructor.name); //Function}constructorName()
可以判断基本数据类型,也可以判断引用数据类型,由于null和undefined没有constructor,所以判断不了。因为constructor.toString().indexOf()也是用到了constructor,所以如果该写了constructor后,就不准确了。
const toStringIndexOf = () => { // 基本数据类型 console.log((1).constructor.toString()); //function Number() { [native code] } console.log((1).constructor.toString().indexOf('Number') > -1) //true console.log(('1').constructor.toString().indexOf('String') > -1) //true // console.log((null).constructor.toString().indexOf('Null') > -1) //报错,Cannot read properties of null (reading 'constructor') // console.log((undefined).constructor.toString().indexOf('Undefined') > -1) //报错, Cannot read properties of undefined (reading 'constructor') console.log((Symbol()).constructor.toString().indexOf('Symbol') > -1) //true console.log((true).constructor.toString().indexOf('Boolean') > -1) //true console.log((NaN).constructor.toString().indexOf('Number') > -1) //true // 引用数据类型 console.log(({}).constructor.toString().indexOf('Object') > -1) //true console.log(([]).constructor.toString().indexOf('Array') > -1) //true console.log((Error('1')).constructor.toString().indexOf('Error') > -1) //true function A() {} console.log((A).constructor.toString().indexOf('Function') > -1) //true}toStringIndexOf()
改写constructor
const num = new Number(1)console.log((num).constructor.name); //Numberconsole.log((num).constructor.toString().indexOf('Number') > -1) //truefunction A() {}num.constructor = Aconsole.log((A).constructor.name); //Functionconsole.log((A).constructor.toString().indexOf('Function') > -1) //true
这里说的类型转换是字符串和数值之间的转换。
toString(),18.toString()
String(),String(18)
拼接,18+''
以上三种办法都可以将数值类型转化为字符串类型。
Number(),Number('18')
parseInt(),parseInt('18')
parseFloat(),parseFloat('18')
+或-,-'18'或者+'18'
以上四种方法都可以将字符串转化为字符串。
我们在定义的时候,分为常量与变量,一般可以使用var,const,let来定义。
es5的时候我们都是用var来定义常量与变量。但是很容易造成变量提升或者变量的全局污染。所以我们现在很少使用这个了。
es6的方法,使用const来定义常量与变量。比如定义基础数据类型的常量,const a = 1,这个就是定义一个a 的常量,不可以更改。这里的不可以更改指的是基础数据类型不能更改,引用数据类型不能更改其类型,但是可以更改其值。
const a = 1const b = { name:'orange', age:18}// 修改aa = 2 //这个肯定要报错的b.age = 20 //这个可以更改
这也是es6的语法,使用let定义变量,不管是基础数据类型还是引用数据类型都可以,同时可以更改。
let a = 1let b = { name:'orange', age:18}// 修改aa = 2b.age = 20b = 3