js变量及类型
编写时间:2019-07-15
更新时间:2019-7-26 8:45作者:鬼小妞
目的:本文旨在介绍
备注: 本文
整理及编写了与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 {});//object
console.log(typeof []);//object
{}是根据new Object()创建的,
[]是根据new Array()创建的,但原型链上有Object.prototype
而通过new来创建的,不是funtion(函数对象)就是object(一般对象,一般对象包括数组[]、日期、标准对象{})
console.log(typeof new Function());//function
console.log(typeof new Object());//object
console.log(typeof new Array());//object
所以,怎么区分数组[]和标准对象{},请点击查看下文如何区分数组对象和普通标准对象{}
1.2.3 为什么对象类型又被称为引用类型
对象类型也称引用类型,为什么对象类型又被称为引用类型?
相对于具有不可变性的原始类型,我习惯把对象称为引用类型,引用类型的值实际存储在堆内存中,它在栈中只存储了一个固定长度的地址,这个地址指向堆内存中的值。
引用类型:当复制保存着对象的某个变量时,操作的是对象的引用,但在为对象添加属性时,操作的是实际的对象。引用类型值指那些可能为多个值构成的对象。
2. 基本类型和(对象类型)引用类型的区别
- 基本类型的变量是存放在栈区的(栈区指内存里的栈内存);
- 引用类型的值是同时保存在栈内存和堆内存中的对象;
- 基本类型的比较是值的比较;
- 引用类型的比较是引用的比较;
- 引用类型包括某些特殊的基本类型包装对象(String、Number、Boolean)
- 引用类型是有方法和属性的,但基本类型是没有的
3. JavaScript 中的基本类型包装对象
除了 null 和 undefined之外,所有基本类型都有其对应的包装对象:
String: 为字符串基本类型。Number: 为数值基本类型。Boolean:为布尔基本类型。Symbol:为字面量基本类型。
这个包裹对象的valueOf()方法返回基本类型值。
我们回过头来对比引用类型(对象类型)和基本类型,你发现了什么:
基本类型:
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));// h
console.log(s2.charAt(0));// h
console.log(typeof s1);//string
console.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
面试题2
//给字符串添加方法 要写到对应的包装对象的原型下才行
var str = 'hello';
String.prototype.number= 10
str.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 运算符查询变量的类型
下表总结了typeof可能的返回值。有关类型和原始值的更多信息,可查看 JavaScript数据结构 页面。
| 类型 | 结果 |
|---|---|
| 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());//function
console.log(typeof new Object());//object
console.log(typeof new Array());//object
console.log(typeof {});//object
console.log(typeof []);//object
console.log(typeof new Object());//object
console.log(typeof new Array());//object
所以,怎么区分数组[]和标准对象{};
具体如何分辨,请点击查看下文提供的方法如何区分数组对象和标准对象
5. 如何区分数组对象[]和普通标准对象{}
注意:typeof是无法分辨标准对象{}和数组对象[]的, 因为: {}=>new Object() []=>new Array()
console.log(typeof {});//object
console.log(typeof []);//object
console.log(typeof new Object());//object
console.log(typeof new Array());//object
具体如何分辨,提供以下方法,并说明原理 你只要明白一点:标准对象{}和数组对象[]就是构造器constructor或构造函数不一样,只要直接或间接知道获取他们的构造函数就可以分辨出来了
(1) instanceof
判断 引用类型 属于哪个 构造函数 的方法
var arr=[];//相当于var arr=new Array();
var obj={};//相当于var obj=new Object();
console.log(arr instanceof Array); //true
console.log(arr instanceof Object); //true
console.log(obj instanceof Array); //false
console.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.prototype
arr.__proto__.__proto__ == Object.prototype
arr.__proto__.__proto__.__proto__ == null
var obj={};
obj.__proto__ == Object.prototype;
obj.__proto__.__proto__ == null;
由上看出,
- 数组arr存在Array.prototype和Object.prototype 所以
console.log(arr instanceof Array); //true
console.log(arr instanceof Object); //true
- 对象obj只存在Object.prototype 所以:
console.log(obj instanceof Array); //false
console.log(obj instanceof Object); //true
(2) constructor构造器
var arr=[];//相当于var arr=new Array();
var obj={};//相当于var obj=new Object();
arr.constructor==Array;//返回true
obj.constructor==Object;//返回true
arr.constructor==Object;//返回false
obj.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);//Array
console.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]" 一般直接判断是否为null
console.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.