js数据类型(基本数据类型,引用数据类型)、堆栈详解

前言:

计算机程序的运行需要对值(value)进行读和写的操作,在编程语言中能够表示并操作的值的类型叫做数据类型(type)。JavaScript的数据类型分为两类:原始类型(primitive type)和对象类型(object type),也可分为不能拥有方法和可以拥有方法的类型的类型,同样可分为不可变(immutable)和可变(mutable)类型类型。可变类型的值是可修改的,对象和数组属于可变类型。数字、布尔值、null和undefined属于不可变类型,比如,修改一个数值的内容本身就说不通。字符串可以看成由字符组成的数组,你可能会认为它是可变的。然而在JavaScript中,字符串是不可变的:可以访问字符串任意位置的文本,但JavaScript并未提供修改已知字符串的文本内容的方法。

一、js中的内存

1、JS内存空间分为栈(stack)、堆(heap),其中栈(stack)存放原始数据类和堆内存的指针、堆(heap)存放引用类型数据的正真值,栈和堆所属空间不能直接访问计算机本地资源;

2、JavaScript的内存管理机制是:在变量(对象,字符串等等数据类型)创建(声明)时分配,然后在他们不再被使用时“自动”释放。后者被称为垃圾回收。

 二、栈(stack):

由电脑分配的固定大小的空间,栈内存中的变量一般都是已知大小或者有范围上限的,算作一种简单存储,栈是一种先进后出的数据结构,栈内存是内存中用于存放临时变量的一片内存块。当声明一个基本变量时,它就会被存储到栈内存中,这种乒乓球的存放方式与栈中存取数据的方式如出一辙。处于盒子中最顶层的乒乓球5,它一定是最后被放进去,但可以最先被使用。而我们想要使用底层的乒乓球1,就必须将上面的4个乒乓球取出来,让乒乓球1处于盒子顶层。这就是栈空间先进后出,后进先出的特点;对于栈中已经有分配内存的,如果对其数据类型进行更改那么在栈中的位置不会改变,但是大小可能会改变。

let a = 20;
let b = a;
b = 30;

在栈内存中的数据发生复制行为时,系统会自动为新的变量分配一个新值,最后这些变量都是相互独立互不影响的

 三、堆(heap):

堆内存存储的对象类型数据对于大小这方面,一般都是未知的,将其指针(地址)存放在栈内存中,指针指向堆内存的真实值,因为使用堆内存中的数据时要在栈中获取其地址再去堆中获取,故而存取速度较慢

let a = { x: 10, y: 20 }
let b = a;
b.x = 5;

a.x // 5

  四、原始类型(primitive type)

JavaScript中的原始数据类类型包括:数字(number),字符串(string),布尔值(boolean),null,undefined。他们的值保存在栈空间,我们通过按值来访问的,内存释放时机比较明显即运行环境中最后一次出现后即可释放内存。

1、数字

javascript不区分整数值和浮点数值,js中的所有数字均用浮点数值表示。js采用IEEE 754标准定义的64位(64位CPU是指CPU内部的通用寄存器的宽度为64比特,支持整数的64比特宽度的算术与逻辑运算,其中)浮点数表示数字,这意味着它能表示的取值范围±1.7976931348623157× 10^308

能够表示的整数范围是从-9 007 199 254 740 992~9 007 199 254 740 992(即-2^53~2^53),包含边界值。如果使用了超过此范围的整数,则无法保证低位数字的精度。

Math.pow(2, 53) ==  Math.pow(2, 53) // true
Math.pow(2, 53) ==  Math.pow(2, 53) + 1 // true

 关于精度问题,下次讨论

2、字符串(string)

javascript中字符串是不可变的,字符串(string)是一组由16位值组成的不可变的有序序列,字符串可以看成由字符组成的数组,可以访问字符串任意位置的文本,但JavaScript并未提供修改已知字符串的文本内容的方法。

16位字符串每个字符所占用的空间为16bits 比特(2 bytes);这是因为JS采用的是unicode编码

不可变字符串对象一旦创建出来,便不能被更改;这可能有些难理解,但事实确实如此。你可能会认为s+='1'只是在s后面增加一个元素1而已,但事实是

  • 先将 s 拷贝一份,记为 temp

  • 在 temp 末尾加上'1'

  • 将 s 变量指向 temp,并删去原来的s可以从字符串对象的许多方法中看出来,如 replace,并不是在原字符串对象上修改,而是会返回一个新的字符串

  • 字符串方法:string.indexOf(‘’)方法返回字符串中指定文本首次出现的索引(位置)slice(first,end)提取字符串的某个部分并在新字符串中返回被提取的部分replace(替换者,被替换)方法用另一个值替换在字符串中指定的值,不会改变调用它的字符串。它返回的是新字符串

有序序列:字符串本身是一个数组,可以通过下表索引,从0开始

3、布尔值(boolean)

这个类型只有两个值,true和false

4、null

null类型被看做空对象指针,只有一个值,即null值,所以在用typeof操作符去检测null类型的值得时候,结果是object类型

5、undefined

只有一个值,即undefined

1、如果声明了一个变量,但是未给变量初始化值,那么这个变量的值就是undefined,undefined是占用内存栈的,js规定undefined占用最小内存。

2、对象没有赋值的属性,该属性的值为undefined

3、函数没有返回值,默认返回undefined

三、引用类型(object type)

值大小不固定,栈内存中存放地址指向堆内存中的对象。是按引用访问的,在堆内存中为这个值分配空间。由于这种值的大小不固定,因此不能把它们保存到栈内存中。但内存地址大小的固定的,因此可以将内存地址保存在栈内存中。 这样,当查询引用类型的变量时, 先从栈中读取内存地址, 然后再通过地址找到堆中的值,进而会导致读取复杂,效率低;对于内存回收判定相对复杂。

对象类型(引用类型)包括:数组(Array),对象(Object),函数(Function)

1、赋值

let a = {}
let b = a
a === b // true

let a = {}
let b = {}
let c = {}
a === b // false

 2、拷贝

对象类型的拷贝分为深拷贝和浅拷贝

1、浅拷贝:

对于对象数据类型来看,浅拷贝是指将数据的栈中的指针进行复制,然而堆内存中的数据没有被复制

let a = { x: 10, y: 20 }
let b = a;
b.x = 5;
a.x // 5

2、深拷贝:

(1)在赋值时等号右边的数据类型为基本数据类型才算完成深拷贝

(2)采用递归去拷贝所有层级属性

 递归拷贝--例

function deepClone(obj){
  let objClone = Array.isArray(obj)?[]:{};
  if(obj && typeof obj==="object"){
      for(key in obj){
          if(obj.hasOwnProperty(key)){
              //判断ojb子元素是否为对象,如果是,递归复制
              if(obj[key]&&typeof obj[key] ==="object"){
                  objClone[key] = deepClone(obj[key]);
              }else{
                  //如果不是,简单复制
                  objClone[key] = obj[key];
              }
          }
      }
  }
  return objClone;
}

(3)通过JSON对象来实现深拷贝 JSON.parse(JSON.stringify(obj))js

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值