资讯专栏INFORMATION COLUMN

基础回顾-javascript数据类型

codergarden / 1187人阅读

摘要:栈区由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。属性返回对创建此对象的构造函数的引用。所有的和都是宿主对象参考高级程序设计堆和栈的概念和区别全面解析中的数据类型与类型转换

数据类型

数据类型划分

javascript中定义了6中基本数据类型(原始值类型),和一种复杂数据类型(引用类型),所谓复杂类型,其本质是由无序的名值对(key:value)组成的。

基本数据类型:

String

Number

Boolean

undefined

null

Symbol (ES6 新增)

复杂数据类型

Object

原始值引用值

上面提到了原始值引用类型,可能有些人对于引用类型很熟悉,但是原始值却很陌生实际上,在ECMAScript中,变量可以存放两种类型的值,即原始值和引用值。

原始值(primitive value)

原始值是固定而简单的值,是存放在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。

引用值(reference value)

引用值则是比较大的对象,存放在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(pointer),指向存储对象的内存处。所有引用类型都集成自Object。

之所以说原始值是固定的,原因是当我们对原始值进行一些操作时结果返回的都是一个新的副本,但是对引用值操作时可能更改原值。

var str = "asdfghjkl";
var obj = {name:1,age:2};

var str2 = str;
var obj2 = obj;

str2 = "lkjhgfdsa";
obj2.name= 3;

console.log(str,str2,obj,obj2)  
//asdfghjkl lkjhgfdsa {name: 3, age: 2} {name: 3, age: 2} 

obj == obj2   //true 

通过以上代码可以明确看出字符串是按值传递的,在赋值时会新建存储空间,将str 和 str2 存放在不同的内存空间内,对象是按引用传递的,obj = obj2时没有新建堆内存空间,而是在栈内存中存放标识符和值的引用地址,引用地址与obj的栈值相同,指向堆内存中的存储空间。

同时可以看到obj == obj2 返回true,这是为什么?

var obj3 = {name:3,age:2}
obj == obj3    //false

obj3 与 obj 的属性和属性值是一样的,但是 obj == obj3 却返回false, obj == obj2 返回true, 这说明引用类型在判断相等的时候比较的是指针,即指向对内存的地址。

提到原始值 引用值 内存地址等词,就不得不提数据的存储空间

数据的存储方式

堆栈

之前说到基本类型存储在栈内存中,复杂类型存储在堆内存中,那么什么是栈,什么是堆?

这里说的堆和栈并不是一种数据结构,而是指存储空间,JVM内存划分为:寄存器,本地方法区,方法区,堆内存,栈内存,我们说的堆栈就是这里的堆内存 和 栈内存。

栈区(stack)

由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

堆区(heap)

一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。

堆栈区别

1.栈内存存储的是局部变量而堆内存存储的是实体;
2.栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;
3.栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收,前提是没有任何引用。

关于堆和栈的内存空间,这里只是简单提起,强调指内存空间并非数据结构。

不同数据类型的存储区别

var a = undefined;
var b = null;
var c = "asdfg";
var d = 123;
var e = true;

var f = [1,2];
var g = {name:1,age:2};
var h = g;

下图解释不同每一种类型的存储方式

上图体现出每种数据类型在内存中的存储方式:

基本类型undefined null String Number Boolean 直接将标识符和值存储在栈区内;

复杂类型对象,数组等,栈区内只存放标识符和指向堆内存中的对象的指针,真真的对象值存储在堆内存中,同一对象的引用指针相同:g == h;

数据类型的判断方式

数据类型的判断有多种方式,下面简单介绍

typeof

typeof 用于检测数据的类型,返回值有

number

string

boolean

undefined

object

function

var u ,a = "asdf",b = 1,c = true,d = {},e = null,f = function(){}
typeof a;  // string
typeof b;  //number
typeof c;  //boolean
typeof u;  //undefined
typeof d;  //object
typeof e;  //object
typeof f;  //function

typeof a == "string"  //true
typeof a == String   //false

以上代码可以看出

返回值为字符串,并且区分大小写;

typeof null 返回 object,因为null是Object的一种,表示空引用,即栈内存储的指向堆中对象的指针为空;

typeof 可以判断function的类型,并且对于非Object类型的数据区分类型比较方便;

不能区分Object的 具体类型,如 Array Date

instanceof

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。

由于js可在多个frame之间交互,但是每个frame有自己的全局环境,所以不同frame和窗口之间Array是不同的,所以无法用 a 环境下的数组去判断是否是 b 环境下的Array对象的实例

var a = "asdffgg";
var b = 1;
var c = new String("aaaa");
var arr = [1,2];
var date = new Date();

a instanceof String  //false   思考:为什么是false?
b instanceof Number  //false
c instanceof Number  //true    思考:a 和 c 同样是字符串结果却不一样
arr instanceof Array   //true
arr instanceof Object  //true    思考:为什么 Array 和 Object都是true?
date instanceof Date  //true
date instanceof Object  //true

typeof a    //string    字面量的形式 返回基本类型 string
typeof c    //object    new 一个String的实例 返回复杂类型 object

通过以上的代码段体现出以下特点和弊端:

instanceof 用于检测Object类型的对象,基本类型的字面量值永远返回false;

instanceof 由于对象的继承关系,所以只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型

数组即是Array的实例,又是Object的实例的原因详解:
由于对象之间存在继承关系,所以instanceof 能够判断出 [ ].__proto__ 指向 Array.prototype,而 Array.prototype.__proto__ 又指向了Object.prototype,最终 Object.prototype.__proto__ 指向了null,标志着原型链的结束。这里关于原型链的知识点不细说

所以无法判断实例对象具体是Object的哪一种类型。

constructor

constructor 属性返回对创建此对象的构造函数的引用。

当定义一个函数Fn的时候,JS引擎会为其添加一个原型prototype,并为原型添加一个constructor属性,将其指向Fn的引用

实例f的constructor是指向其构造函数Fn的,所以通过constructor我们可以判断其构造函数是谁,从而间接的判断对象的具体类型
但是由于constructor在实现继承的时候可以被更改,实质更改是由于子类继承父类的时候可能重写了子类的原型prototype,因此使子类的prototype上的constructor发生变化,因此类型判断可能不准确

undefined 和 null没有constructor属性

toString

Object.prototype.toString 方法可以返回对象的内部属性[[Class]],格式为[object,Xxxx],其中Xxxx为具体的类型

Object.prototype.toString.call("") //"[object String]"

Object.prototype.toString.call([1]) //"[object Array]"

Object.prototype.toString.call(new Date) //"[object Date]"

为什么不调用对象的toString方法,而是通过call调用Object.prototype.toString?

"11".toString() //"11"
[1,2].toString() //"1,2"
new Date().toString() //"Fri Jun 22 2018 14:44:52 GMT+0800 (CST)"

可以看到字符串输出的是本身,数组输出的是“,”链接的数组字符串,时间类型输出的时间字符串,都不是内部属性[[Class]],原因是实例的构造函数重写了toString方法,所以要吊用Object对象的原型伤的toString方法。

类型转换

转数值类型

显示转换:Number() parseInt() parseFloat()

向Number 转换规则 ToNumber

Boolean类型的true false,转成1 和 0

数值直接转成原始值,即本身

null 转为0,undefined 转为NaN

字符串类型,数值字符串转为数值型数字;空字符串""转为0;非数字字符串转为NaN

对象,则调用对象的valueOf()方法,按前面规则转为原始值,如果返回NaN,调用对象的toString()方法,返回值再依照前面规则转数值

var a = {a:1}
Number(11)        //11
Number("123")     //123
Number(true)      //1
Number(null)      //0
Number(undefined) //NaN
Number("")        //0
Number(a)     //NaN   a.valueOf返回 {a:1},a.toString 返回 "[object,Object]",字符串转数值 返回NaN

parseInt parseFloat 这里不细说

隐式转换:操作符

+做为一元操作符,操作数转化为数值类型

+作为二元操作符,两个操作数中只要有一个是字符串类型,那么另一个也转化成字符串类型

+作为二元操作符,两个操作数均不是字符串类型,那么两个操作数均各自隐式向数值型转化,然后在计算

如果数值计算的操作符不是+操作符,那么操作数向数值转化

转String类型

显示转换 toString() String()

toString:返回相应的字符串表现,

向字符串转换规则 ToString

数字类型,转为数字字符串

字符串,返回原值

undefined null,没有toString方法,可以用String()方法,返回"null"和"undefined"字符串

Boolean 转为"true" "false"

对象,先调用toString得到原始值,如果是原始值类型则返回;不是则调用valueOf,返回结果如果是原始值类型则返回,不是在做toString调用,返回结果

var a = {a:1};var b = 11;var c = "11"; var e = "[1,3]";var d = true

a.toString()  //"[object,Object]"
b.toString()  //"11"
c.toString()  //"11"
d.toString()  //"true"
e.toString()  //"1,3"

隐式转换同+操作符数值转化

转Boolean类型

显示转换:Boolean()

向Boolean转换规则:

数字0,""空字符串,null,undefined,NaN转为false,其余转为true

隐式转换操作符 if

什么是隐式转换?

隐式转换与执行环境和操作符相关,当前操作期望某种值的时候,就会发生隐式转换,实际上面提到的具体规则点就是隐式转换的过程。

内置对象(本地对象)、单体内置对象,宿主对象

本地对象:不依赖于宿主环境的对象
Object Array Date Function RegExp String Boolean Number

单体内置对象:由ECMAScript实现提供的,不依赖于宿主环境的对象,这些对象在ECMAScript程序执行前就已经存在了
Global(所有不属于其他任何对象的属性和方法都属于Global,全局变量,方法),Math,一些数学公式和计算方法

宿主对象:由ECMAScript实现的宿主环境提供的对象,可以理解为:浏览器提供的对象。所有的BOM和DOM都是宿主对象 , Window

参考

javascript 高级程序设计
堆和栈的概念和区别
全面解析js中的数据类型与类型转换

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95802.html

相关文章

  • JavaScript 回顾学习:变量

    摘要:变量的分类全局变量可以在任何地方使用的变量局部变量在函数内声明的变量只在函数内有定义,作用域是局部性的什么时候使用当一个数据需要被反复使用时,就要先保存在变量中。 第一篇回顾学习,变量 什么是变量 变量就是在内存中刨一个坑存一个数据,再给这个坑起个名。为什么要给一个加引号呢,因为JavaScript是松散类型的,即一个变量可以用来保存任何类型的数据。变量的分类:全局变量:可以在任何地方...

    KitorinZero 评论0 收藏0
  • javascript基础篇小结

    摘要:表示尚未存在的对象是一个有特殊意义的值。可以为变量赋值为,此时变量的值为已知状态不是,即。用来初始化变量,清除变量内容,释放内存结果为但含义不同。且它俩与所有其他值比较的结果都是。,需要两个操作数同时转为。 转载请声明出处 博客原文 随手翻阅以前的学习笔记,顺便整理一下放在这里,方便自己复习,也希望你有也有帮助吧 第一课时 入门基础 知识点: 操作系统就是个应用程序 只要是应用...

    hiyang 评论0 收藏0
  • 前端面试回顾(1)---javascript的面向对象

    摘要:每个类有三部分构成第一部分是构造函数内,供实例对象化复制用。第二部分是构造函数外,直接通过点语法添加,供类使用,实例化对象访问不到。组合继承还有一个要注意的地方在代码处,将子类原型的属性指向子类的构造函数。 前言 前一阵面试,过程中发现问到一些很基础的问题时候,自己并不能很流畅的回答出来。或者遇到一些基础知识的应用,由于对这些点理解的不是很深入,拿着笔居然什么都写不出来,于是有了回顾一...

    animabear 评论0 收藏0
  • JS基础知识回顾-1

    摘要:原始类型数组,循环变量不能以数字开头,因为如果这样编译器则无法区别数字和变量。可以直接修改数组的值如就变成了输出为循环和判断 Part1 原始类型,数组,循环 Variables 变量不能以数字开头,因为如果这样编译器则无法区别数字和变量。 养成好习惯每句话后面加分号 Primitive data types 包括Number,String, Boolean, Undefined...

    CoorChice 评论0 收藏0
  • JavaScript回顾学习:目录篇

    摘要:前言之前说要重头开始复习,之前一直都在忙着找工作面试,现在工作也终于是找到了,虽然不那么尽人意,但总算有个归属,一段时间的适应也有了自己稳定的时间规划,为了给懒惰捉急的自己一个前行的动力,这一篇的回顾学习目录篇也就这样提上了日程。 前言 之前说要重头开始复习js,之前一直都在忙着找工作面试,现在工作也终于是找到了,虽然不那么尽人意,但总算有个归属,一段时间的适应也有了自己稳定的时间规划...

    luckyw 评论0 收藏0

发表评论

0条评论

codergarden

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<