js变量及类型
编写时间:2019-07-15
更新时间:2019-7-26 8:45作者:鬼小妞
目的:本文旨在介绍
- ,
- JS内存(待更新)
备注: 本文
整理及编写
了与js变量及类型相关的一些知识,仅供参考,描述不当的地方,请评论指正状态:
待更新~
2019-7-26
[TOC]
1. JS的数据类型
最新的ECMAScript标准规定了8种数据类型,其把这8种数据类型又分为两种:原始类型和对象类型。
1.1 原始类型
-
Null:只包含一个值:null
-
Undefined:只包含一个值:undefined
-
Boolean:包含两个值:true和false
-
Number:整数或浮点数,还有一些特殊值(-Infinity、+Infinity、NaN)
-
BigInt: 大于等于2^53的所有数字
-
String:一串表示文本值的字符序列
-
Symbol:一种实例是唯一且不可改变的数据类型
(在es10中加入了第七种原始类型
BigInt
,现已被最新Chrome支持)在JavaScript中,Number 可以准确表达的最大数字是2^53-1,大于等于2^53的所有数字可以使用BigInt表达。
在ECMAScript标准中,它们被定义为primitive values,即原始值,代表值本身是不可被改变的。
1.2 对象类型(引用类型)
- 对象类型(引用类型):除了常用的Object,Array、Function等都属于特殊的对象
- "标准的" 对象, 和函数
- 日期:Date
- 有序集: 数组Array和类型数组
- 键控集: Maps, Sets, WeakMaps, WeakSets
- 结构化数据: JSON
- 标准库中更多的对象请查看
根据上面的对象类型(引用类型)列举的具体有以下常见的几种 对象类型(引用类型)有这几种:
- object、
- Array、
- RegExp、
- Date、
- Function、
特殊的基本类型包装对象(String、Number、Boolean)
- 单体内置对象(Global、Math)
1.2.1 请区分对象类型、普通对象、函数对象
(1). 对象类型
- 对象类型是包含基本对象,而基本对象包括一般对象、函数对象和错误对象。
- Object 、Function 是 JS 自带的函数对象 (请查看)。
所以 ,你会发现,在使用typeof 运算符查询变量的类型时,结果可能值参考里会出现function
,例如:
console.log(typeof Object); //function console.log(typeof Function); //function 复制代码
得到的都是function,即函数对象
如何区分一般对象和函数对象 凡是通过 new Function() 创建的对象都是函数对象,其他的都是一般对象(一般对象包括数组[]、日期、标准对象{})
使用typeof,如果输出值是
- function——————函数对象
- object————————一般对象
(2). 标准对象
基本对象包括一般对象、函数对象和错误对象。 这里的一般对象包括数组、日期、标准对象等, 凡是通过 new Function() 创建的对象都是函数对象,其他的都是一般对象(一般对象包括数组[]、日期、标准对象{}); 标准对象就是我们创建的{},标准对象 是通过new Object()创建的,比如如下方式创建的:
1. var o1 = {}; 2. var o2 =new Object();3. var o3 = new f1();复制代码
01和03归根结底都是通过new Object()生成的,我们可以根据typeof
来验证,如果输出object,那就表明是一般对象(一般对象包括数组[]、日期、标准对象{})。
console.log(typeof o1); //object console.log(typeof o2); //object console.log(typeof o3); //object复制代码
(3). 函数对象
基本对象包括一般对象、函数对象和错误对象。 凡是通过 new Function() 创建的对象都是函数对象,其他的都是一般对象(一般对象包括数组[]、日期、标准对象{}); 函数对象是通过new Function()创建的,比如如下方式创建的:
1. function f1(){}; 2. var f2 = function(){};3. var f3 = new Function('str','console.log(str)');复制代码
f1和f2归根结底都是通过New Function()生成的,我们可以根据typeof
来验证,如果输出object,那就表明是一般对象(一般对象包括数组[]、日期、标准对象{})
console.log(typeof f1); //function console.log(typeof f2); //function console.log(typeof f3); //function 复制代码
1.2.2 如何区分数组[]和标准对象{}
-
数组Array:是可索引的集合对象,根据new Array()创建来的
-
标准对象Object:是基本对象,根据new Object()创建来的
我们都知道一般对象包括数组、日期、标准对象。 所以使用typeof校验出的结果为object的是一般对象
(一般对象包括数组[]、日期、标准对象{}), 请看以下输出:
console.log(typeof {});//objectconsole.log(typeof []);//object复制代码
{}是根据new Object()创建的,
[]是根据new Array()创建的,但原型链上有Object.prototype
而通过new来创建的,不是funtion(函数对象)就是object(一般对象,一般对象包括数组[]、日期、标准对象{})
console.log(typeof new Function());//functionconsole.log(typeof new Object());//objectconsole.log(typeof new Array());//object复制代码
所以,怎么区分数组[]和标准对象{},请点击查看下文
1.2.3 为什么对象类型又被称为引用类型
对象类型也称引用类型,为什么对象类型又被称为引用类型?
相对于具有不可变性的原始类型,我习惯把对象称为引用类型,引用类型的值实际存储在堆内存中,它在栈中只存储了一个固定长度的地址,这个地址指向堆内存中的值。
引用类型
:当复制保存着对象的某个变量时,操作的是对象的引用,但在为对象添加属性时,操作的是实际的对象。引用类型值指那些可能为多个值构成的对象。
2. 基本类型和(对象类型)引用类型的区别
- 基本类型的变量是存放在栈区的(栈区指内存里的栈内存);
- 引用类型的值是同时保存在栈内存和堆内存中的对象;
- 基本类型的比较是值的比较;
- 引用类型的比较是引用的比较;
- 引用类型包括某些特殊的基本类型包装对象(String、Number、Boolean)
- 引用类型是有方法和属性的,但基本类型是没有的
3. JavaScript 中的基本类型包装对象
除了 null 和 undefined之外,所有基本类型都有其对应的包装对象:
String
: 为字符串基本类型。Number
: 为数值基本类型。Boolean
:为布尔基本类型。Symbol
:为字面量基本类型。
这个包裹对象的方法返回基本类型值。
我们回过头来对比引用类型
(对象类型)和基本类型
,你发现了什么:
基本类型:
String、Number、Boolean
、Null、Undefined、BigInt、Symbol引用类型:
String、Number、Boolean
、Object、Array、Date、Function、Error、RegExp、Math、Globle。
you get!! 2者都有String、Number、Boolean
,但是:
- 基本类型是没有属性和方法的,这三个基本类型
String、Number、Boolean
都有自己对应的包装对象。 - 引用类型(对象类型)的
String、Number、Boolean
是表示特殊的基本类型包装对象(String、Number、Boolean)
,包装对象呢,其实就是对象,有相应的属性和方法。
所以,我们以前肯定见过这样的代码:
var s1 = 'hello world'; //string 基本类型var s2 = new String('hello world'); //object 字符串对象 console.log(s1.charAt(0));// hconsole.log(s2.charAt(0));// h console.log(typeof s1);//stringconsole.log(typeof s2);//object复制代码
str 虽然是基本类型,但是它却有方法,怎么来的呢?
var s1 = 'hello world'; s1.charAt(0)调用方法,后台会执行以下操作:复制代码
-
基本类型找到对应的包装对象类型,然后通过包装对象创建出一个和基本类型值相同的对象s1;
-
然后这个对象s1就可以调用包装对象下的方法,并且返回
-
销毁这个临时创建的对象, s1 =null;
例1:
var str = 'hello'; //string 基本类型var s2 = str.charAt(0); //在执行到这一句的时候 后台会自动完成以下动作 : 【 var str = new String('hello'); // 1 找到对应的包装对象类型,然后通过包装对象创建出一个和基本类型值相同的对象 var s2 = str.chaAt(0); // 2 然后这个对象就可以调用包装对象下的方法,并且返回结给s2. str = null; // 3 之后这个临时创建的对象就被销毁了, str =null; 】 alert(s2);//h alert(str);//hello 注意这是一瞬间的动作 实际上我们没有改变字符串本身的值。就是做了下面的动作.这也是为什么每个字符串具有的方法并没有改变字符串本身的原因。复制代码
实际上我们没有改变字符串本身的值。就是做了下面的动作.这也是为什么每个字符串具有的方法并没有改变字符串本身的原因。
面试题1
var str = 'hello';str.number = 10; //假设我们想给字符串添加一个属性number ,后台会有如下步骤 【 var str = new String('hello'); // 1 找到对应的包装对象类型,然后通过包装对象创建出一个和基本类型值相同的对象 str.number = 10; // 2 通过这个对象调用包装对象下的方法 但结果并没有被任何东西保存 str =null; // 3 这个对象又被销毁 】 alert(str.number); //undefined 当执行到这一句的时候,因为基本类型本来没有属性,后台又会重新重复上面的步骤 【 var str = new String('hello'); // 1 找到基本包装对象,然后又新开辟一个内存,创建一个值为hello对象 str.number = undefined // 2 因为包装对象下面没有number这个属性,所以又会重新添加,因为没有值,所以值是未定 ;然后弹出结果 str =null; // 3 这个对象又被销毁 】 复制代码
所以,基本类型无法直接添加属性和方法,那么我们怎么才能给基本类型添加方法或者属性呢?
答案是在基本包装对象的原型下面添加,即使销毁了这个对象,但是原型对象上的属性和方法还是保存了下来,后面调用的时候,就可以在它的原型对象上重新获取这些方法。每个对象都有原型。请看
面试题2
//给字符串添加方法 要写到对应的包装对象的原型下才行var str = 'hello';String.prototype.number= 10str.number; // 5 执行到这一句,后台依然会偷偷的干这些事 【 var str = new String('hello');// 找到基本包装对象,new一个和字符串值相同的对象, str.number; // 通过这个对象找到了包装对象下的属性并调用 str =null; // 这个对象被销毁 】复制代码
如何添加方法:
//给字符串添加方法 要写到对应的包装对象的原型下才行var str = 'hello';String.prototype.last= fuction(){ return this.charAt(this.length);}; str.last(); // 5 执行到这一句,后台依然会偷偷的干这些事 【 var str = new String('hello');// 找到基本包装对象,new一个和字符串值相同的对象, str.last(); // 通过这个对象找到了包装对象下的方法并调用 str =null; // 这个对象被销毁 】复制代码
4. 使用typeof 运算符查询变量的类型
如何查询判断变量的类型? 使用typeof 运算符查询变量的类型
下表总结了可能的返回值。有关类型和原始值的更多信息,可查看 页面。
类型 | 结果 |
---|---|
Undefined | "undefined" |
Null | "object" |
Boolean | "boolean" |
Number | "number" |
String | "string" |
函数对象([[Call]] 在ECMA-262条款中实现了) | "function" |
Symbol(ECMAScript 6 新增) | "symbol" |
任何其他对象 | "object" |
宿主对象(由JS环境提供) | Implementation-dependent |
注意:typeof是无法分辨标准对象{}和数组对象[]的, 因为:
{}是根据new Object()创建的,
[]是根据new Array()创建的,但原型链上有Object.prototype
而通过new来创建的,不是funtion(函数对象)就是object(一般对象,一般对象包括数组[]、日期、标准对象{})
console.log(typeof new Function());//functionconsole.log(typeof new Object());//objectconsole.log(typeof new Array());//object复制代码
console.log(typeof {});//objectconsole.log(typeof []);//objectconsole.log(typeof new Object());//objectconsole.log(typeof new Array());//object复制代码
所以,怎么区分数组[]和标准对象{};
具体如何分辨,请点击查看下文提供的方法
5. 如何区分数组对象[]和普通标准对象{}
注意:typeof是无法分辨标准对象{}和数组对象[]的, 因为: {}=>new Object() []=>new Array()
console.log(typeof {});//objectconsole.log(typeof []);//objectconsole.log(typeof new Object());//objectconsole.log(typeof new Array());//object复制代码
具体如何分辨,提供以下方法,并说明原理 你只要明白一点:标准对象{}和数组对象[]就是构造器constructor或构造函数不一样,只要直接或间接知道获取他们的构造函数就可以分辨出来了
(1)
判断 引用类型
属于哪个 构造函数
的方法
var arr=[];//相当于var arr=new Array();var obj={};//相当于var obj=new Object();console.log(arr instanceof Array); //trueconsole.log(arr instanceof Object); //trueconsole.log(obj instanceof Array); //falseconsole.log(obj instanceof Object); //true复制代码
instanceof运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置;
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
instanceof运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置; 也就是说比如
var arr=[];console.log(arr instanceof Array); //true 复制代码
因为某些浏览器标识__proto__ 相当于 constructor.prototype
所以arr instanceof Array
就是相当于以下代码
if(arr.__proto__ == Array.prototype)//或者if(arr.constructor.prototype == Array.prototype)复制代码
为什么会这样?这要涉及到原型与原型链:
instanceof 判断数组或对象的原理
但是由于
var arr=[];arr.__proto__ == Array.prototypearr.__proto__.__proto__ == Object.prototypearr.__proto__.__proto__.__proto__ == nullvar obj={};obj.__proto__ == Object.prototype;obj.__proto__.__proto__ == null;复制代码
由上看出,
- 数组arr存在Array.prototype和Object.prototype 所以
console.log(arr instanceof Array); //trueconsole.log(arr instanceof Object); //true复制代码
- 对象obj只存在Object.prototype 所以:
console.log(obj instanceof Array); //falseconsole.log(obj instanceof Object); //true复制代码
(2) constructor构造器
var arr=[];//相当于var arr=new Array();var obj={};//相当于var obj=new Object();arr.constructor==Array;//返回trueobj.constructor==Object;//返回truearr.constructor==Object;//返回falseobj.constructor==Array;//返回false复制代码
constructor 判断数组或对象的原理
也是和原型及原型链相关的
var arr=[];//相当于var arr=new Array();复制代码
arr是根据new关键字创建的数组对象,所以,arr的构造函数就是Array。
(3) constructor.name
返回构造函数的名称
var arr=[];//相当于var arr=new Array();var obj={};//相当于var obj=new Object();console.log(arr.constructor.name);//Arrayconsole.log(obj.constructor.name);//Object复制代码
(4) Object.prototype.toString.call()
Object.prototype.toString.call()返回[object constructorName]
的字符串格式,这里的constructorName就是call参数的函数名
console.log(Object.prototype.toString.call([]));//返回"[object Array]"console.log(Object.prototype.toString.call({}));//返回"[object Object]"console.log(Object.prototype.toString.call("str"));//返回"[object String]"console.log(Object.prototype.toString.call(true));//返回"[object Boolean]"console.log(Object.prototype.toString.call(3));//返回"[object Number]"console.log(Object.prototype.toString.call(null));//返回"[object Null]" 一般直接判断是否为nullconsole.log(Object.prototype.toString.call(undefined));//返回"[object undefined]"一般直接判断是否为Undefined复制代码
6. JS内存
在JavaScript中,每一个变量在内存中都需要一个空间来存储。
内存空间又被分为两种,栈内存与堆内存。
栈内存 ————原始类型:
-
存储的值大小固定
-
空间较小
-
可以直接操作其保存的变量,运行效率高
-
由系统自动分配存储空间
JavaScript中的原始类型的值被直接存储在栈中,在变量定义时,栈就为其分配好了内存空间。
堆内存————引用类型:
-
存储的值大小不定,可动态调整
-
空间较大,运行效率低
-
无法直接操作其内部存储,使用引用地址读取
-
通过代码进行分配空间
相对于具有不可变性的原始类型,我习惯把对象称为引用类型,引用类型的值实际存储在堆内存中,它在栈中只存储了一个固定长度的地址,这个地址指向堆内存中的值。
-----以下内容待整理-------------
6.1 为什么要尽量使用基本变量类型,避免使用包装对象?
请先了解以下概念: 1.基本类型:String、Number、Boolean、Null、Undefined、BigInt、Symbol 2.引用类型:特殊的基本类型包装对象(String、Number、Boolean)、Object、Array、Date、Function、Error、RegExp、Math、Globle...... 3.