写在前面

最近在读《JavaScript启示录》,这本书不是JavaScript的详尽的参考指南,但是把对象作为了解JavaScript的透镜,受益匪浅。

那么我们先来聊一下JavaScript的原始值(值类型)以及复杂值(引用类型),以及他们在内存空间中的存储,关于他们你可能不清楚的一些事:

我们先通过一个经典的面试题类型(并不是原题,我即兴发挥)引出我们今天的主题:

  

  

我们已经看出他们的差别,在图一:我们让b = a,改变b的值,发现a并没有改变。在图二:我们让d = c,通过d.name改变对象的name属性,发现c.name也变化了。

事实上,原始值存储在栈内存中,按值来访问。复杂值(引用类型)在堆内存里面,按引用地址访问;然后我们会想到局部变量和全局变量在内存中的存储:下面是我在一个群中给一个同行的回答(前辈们莫见笑)

  

下面会具体介绍复杂值、原始值以及他们的一些特性与内存空间:

  

1、原始值是非对象

我们老生常谈的JavaScript五大基本的数据类型,null、undefined、number、string、boolean都被视为原始值,因为他们是不可细化的,本身是简单的,不能表示由其他的值组成的值。

这里需要注意的是:与使用字面量语法创建相反,在使用new关键字创建的String,Number,或Boolean值时,创建的实际上是一个复杂对象,此时已不在是原始值。

  a、下面对原始值和原生JavaScript对象之间的差异进行了比较:

  

需要注意没有使用new关键词,从构造函数返回的字符、数字、布尔值 对比 使用字面量方法所创建的仍然不是对象。

  b、我们在来对比一下使用new关键字创建的构造函数:

  

除了new出来的Function()对象返回的是function,其他都是object,其实在JavaScript中对函数定义非常高,因此在引用类型中,typeof能检测出函数的详细类型。

上述代码可以告诉我们:原始值不是对象,原始值的特殊之处是用于表示简单值;

2、原始值的赋值,存储,比较方式

  a、原始值在“ 面值(face value)”中的存储和操作,理解这一点非常重要,因为原始值是真实值的复制

  

这里的重点是,原始值是作为不可细化的值进行存储和操作的,引用他们会转移其值:这里的意思也就是原始值(值类型)在内存中每一个值都会存储在对应的变量的中去,也就是一个真实值的”复制”。

  b、原始值的比较采用值比较

我们通过比较原始值来确定其值在字面上是否相同

通过下面的代码来理解“值比较“的概念,并将它与复杂数字进行比较:

  

这里的重点是,在进行比较时,原始值会去检查表示的值是否相等,这里我们要特别和复杂值进行比较(因为复杂值不会去比较值是否相等,而是比较引用地址是否相同)

3、原始值(String,Number,Boolean)在被用做对象时就像对象

null和undefined都是非常简单的值,它们不需要构造函数,也没有new操作为自己创建JavaScript值(可以把他们当做操作符来使用即可)

原始值被当做构造函数创建的一个对象来使用时(注意不使用new),JavaScript会把其转化为一个对象,以便可以使用对象的特性(如方法),而抛弃对象的性质,并将它返回到原始值。

  

上述实例代码,所有的原始值(除null、undefined)都被转化为对象,以便充分利用toString()方法。一旦调用和返回改方法,对象就会被转换成对象值。这样我相信我们能很好的理解标题了

4、复杂值(复合对象、引用类型)

本质上,复杂对象其在内存中的大小是未知的,因为复杂对象可以包含任何值:

下面通过字面量的方法创建一个对象和数组

  

相比简单的原始值,原始值不能表示复杂值,而复杂值可以封装任意的JavaScript值

5、如何存储或复制复杂值

复杂值是通过引用来进行存储和操作的,这就回到了开始那个问题的图二,理解这一点非常重要。创建一个包含复杂对象的变量时,其值是内存中的一个引用地址。引用一个复杂对象时,使用它的名称(即变量或对象属性)通过内存中的引用地址获取对象值。当我们试图复制一个复杂值的时候,理解这就非常重要了。复杂值复制的过程、其实并不是复制对象,更多的是像复制对象的地址

  

所以就像上面说过的,复制的是内存堆栈中对象的地址或者引用。

6、复杂对象比较采用引用比较

也就是说:复杂对象只有在引用相同的对象(即有相同的引用地址)时才相等:

  

我相信我们已经理解:指向内存中复杂对象的变量,只有在引用相同对的‘地址’的情况下才是相等的,相反,两个单独创建的对象、即使具有相同的类型并拥有完全相同的属性,他们也是不相等的。

7、复杂对象具有动态属性

通过这一点,我们可以根据需求为复杂对象有任意多个引用。

  

上述代码,objA、pointer1、pointer2都引用了内存中的同一对象

  

【emoji罒ω罒】这三个每次调用对象的方法都会叫他‘一个人’

  

复杂对象支持动态对象属性,因为我们可以定义对象,然后创建引用,在更新对象、并且所有指向该对象的变量都会’获得’更新.

8、动态属性支持异变对象

复杂对象是由动态属性构成的,这一点非常重要,这使得用户自定义对象和大多数原生对象产生突变。通过增加原生对象、来改变JavaScript本身的原生预配置特性:

下面我们在原生构造函数上存储属性,并在原型对象上,向原生对象添加新方法:

  

所以我们明白,JavaScript的对象是动态的,这使得JavaScript对象是可变的。通过自定义我们改变了原生内部的运行机制,你会获得一个自定义版本的JavaScript来处理程序,但是使用一定要谨慎。

9、两个存储空间:栈&&堆

我们前面也提到了存储空间,在程序运行时,有两个存储空间可用,一个是栈,归属进程本身的;另一个是堆,所有进程共用的:

然后就很好理解了,因为局部变量声明在函数周期内部,在函数结束时其生命周期也就结束了,其存储空间位于栈中,当进入函数时,会根据函数内部需求,在栈申请一段内存空间,供局部变量使用。当局部变量生命周期结束后(该函数结束),在栈上释放。

由于进程栈的空间是有限的,所以1)要避免申请占用空间较大的局部变量,2)避免函数嵌套层数过多,这些都可能引起栈空间不够导致程序崩溃。

   关于数据结构中栈和堆,后面还会进一步的学习总结,比如管理方式、申请大小、碎片问题、分配方式、分配效率...

写在后面

相信到这里我们对js中的原始值、复杂值、以及他们的特性、在内存中的存储有了比较深入的理解,那么让我们开始准确我们的JavaScript世界观系列,因为我从高中毕业后接触前端,对原生的热爱程度远远大于jQuery等类库。如果想实现JavaScript类库或者框架,应该打开“引擎盖”看看,了解发动机的情况。

 

复杂值vs原始值&&内存空间的更多相关文章

  1. (复杂值vs原始值)&&内存空间 — 准确我们的JavaScript世界观(一):

    写在前面 最近在读<JavaScript启示录>,这本书不是JavaScript的详尽的参考指南,但是把对象作为了解JavaScript的透镜,受益匪浅. 那么我们先来聊一下JavaScr ...

  2. EF里查看/修改实体的当前值、原始值和数据库值以及重写SaveChanges方法记录实体状态

    本文目录 查看实体当前.原始和数据库值:DbEntityEntry 查看实体的某个属性值:GetValue<TValue>方法 拷贝DbPropertyValues到实体:ToObject ...

  3. javascript中可变值与不可变值(原始值)

    字符串原始值修改不了1 var str = "abc"; 2 str[0] = "d"; 3 console.log(str[1]="f") ...

  4. javascript中的原始值和复杂值

    × 目录 [1]特性 [2]存储方式 [3]访问方式 [4]比较方式 [5]动态属性 前面的话 javascript的数据类型可以分为两种:原始类型和引用类型.原始类型也称为基本类型或简单类型,jav ...

  5. Js 中的原始值和引用值

    最近遇写 node.js 时到一个问题,把对象当赋值给数组成员时总是出错,比如下面的代码, var Arr = new Array(); var Obj = new Object(); for(var ...

  6. JS中原始值和引用值分析

    JS中变量中两种类型的值:原始值,引用值 原始值是存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置. var x = 1; //1就是一个原始值,变量x中存放的就是原始 ...

  7. ES6 学习笔记(三)原始值与引用值

    总结: 1.原始值,表示单一的数据,如10,"abc",true等. 1.1. ES的6种原始值: Undefined.Null.Boolean.Number.String.Sym ...

  8. JavaScript数据操作--原始值和引用值的操作本质

    我的一句话总结:原始值不管是变量赋值还是函数传递都不会改变原值,引用值不管是变量赋值还是函数传递,如果新变量重新赋值,则不会影响原引用值,如新变量是直接操作,就会影响原引用值. 首先明确,值和类型是两 ...

  9. JavaScript中对象转换为原始值的规则

    JavaScript中对象转换为原始值遵循哪些原则? P52 对象到布尔值对象到布尔值的转换非常简单:所有的对象(包括数字和函数)都转换为true.对于包装对象亦是如此:new Boolean(fal ...

随机推荐

  1. C盘下出现msdia80.dll文件

    删除方法 https://jingyan.baidu.com/article/63acb44acef55661fdc17e56.html 或者 https://www.cnblogs.com/ggll ...

  2. mysql8.0 安装

    之前一直使用的是zabbix3.4和mariadb5.5;感觉良好!!!但是...因其他原因需要重新部署 这次规划使用zabbix4.0和mysql8.0结合使用:嗯~~应该不会错吧!!! zabbi ...

  3. November 28th 2016 Week 49th Monday

    You only live once, but if you do it right, once is enough. 年华不虚度,一生也足矣. One today can win two tomor ...

  4. 通过 Chrome 调试运行在 IOS-safari 上的页面

    本文重点讨论如何在 Windows 系统中通过chrome 浏览器调试运行在 iPhone Safari 浏览器中的网页.如果你有一台 iMac/MacBook,可忽略该文档.iMac 环境下,直接通 ...

  5. js实现svg图形转存为图片下载[转]

    我们知道canvas画布可以很方便的js原生支持转为图片格式并下载,但是svg矢量图形则并没有这方面原生的支持.研究过HighChart的svg图形的图片下载机制,其实现原理大体是浏览器端收集SVG代 ...

  6. mybatis #{}和${}的区别是什么

    #{}和${}的区别是什么?正确的答案是:#{}是预编译处理,${}是字符串替换.(1)mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法 ...

  7. [USACO09OPEN]Ski Lessons

    嘟嘟嘟 先考虑这两点: 1.如果我们有结束时间相同的课程,且达到的能力相同,那么我们一定选择开始时间最晚的. 2.如果有能力值相同的滑雪坡,我们一定选择时间最短的. 因此先预处理两个数组.cla[i] ...

  8. Hadoop学习之路(十七)MapReduce框架Partitoner分区

    Partitioner分区类的作用是什么? 在进行MapReduce计算时,有时候需要把最终的输出数据分到不同的文件中,比如按照省份划分的话,需要把同一省份的数据放到一个文件中:按照性别划分的话,需要 ...

  9. Hadoop学习之路(三)Hadoop-2.7.5在CentOS-6.7上的编译

    下载Hadoop源码 1.登录官网 2.确定你要安装的软件的版本 一个选取原则: 不新不旧的稳定版本 几个标准: 1)一般来说,刚刚发布的大版本都是有很多问题 2)应该选择某个大版本中的最后一个小版本 ...

  10. Python自动化之高级语法单例模式

    方法1 共享属性;所谓单例就是所有引用(实例.对象)拥有相同的状态(属性)和行为(方法) 同一个类的所有实例天然拥有相同的行为(方法), 只需要保证同一个类的所有实例具有相同的状态(属性)即可 所有实 ...