Js的内存机制

198 阅读3分钟

Js的语言类型

Js是一种 弱类型动态 的语言。

动态是什么意思呢,动态是在使用变量前不需要确定变量的类型,在给变量赋值的时候确定变量的值。

弱类型是什么呢,弱类型是支持 隐形转换 的语言。举个例子给一个变量赋为整数,再给这个变量赋为字符串,系统会把这个变量的类型自动从int转为String。

a=1
a="String"-----系统会自动把a的类型改为字符串类型

Js的数据类型

Js的数据类型有很多,从储存的方式可以分为 基础类型引用类型 ,我写了一些数据类型,前7种都是基础类型,后三种都是应用类型。 栈很小,基础类型因为一般比较小,所有直接放在栈中,引用类型一般都比较大所以放在堆中,只在栈中放一个地址,指向堆中放的数据。

let a = 1
a = 'hello'
a = true
a = null
a = undefined
a = Symbol(1)
a = 123n

a = []
a = {}
a = function () {}
  • 基础类型 是直接储存在栈上,不用储存在堆上
  • 引用类型 是在栈上存储一个地址,地址指向在堆上存储的对象 image.png 这里从内存的角度解释两种类型的生成过程
function foo() {
  var a = 1
  var b = a
  var c = { name: 'zzz' }
  var d = c
}
foo()
  1. foo在全局作用域中被调用,生成foo的函数作用域
  2. 在foo的函数作用域上,直接给基础类型的变量赋值
  3. 给引用类型的变量在堆上申请地址,把对象放入,然后返回一个地址给引用类型的变量

image.png

为什么要把栈设计的很小,不把所有的东西都放进去呢

因为栈是用来维护函数的调用关系的,如果设计的过大,程序员就可能肆无忌惮的,调用函数,让一个函数调用链变得非常长,函数之间的切换就会变得很慢。就像把一个口袋设计的过长,往里面放东西,掏东西的话就会很麻烦。而且如果你把你的函数调用关系写的栈都放不下,更应该反省自己是不是在写屎山代码。

写个编程题

写出下列代码的输出结果

function foo() {
  var myname = '牛哥'
  let test1 = 1
  const test2 = 2
  var innerBar = {
    setName: function (name) {
      myname = name
    },
    getName: function () {
      console.log(test1);
      return myname
    }
  }
  return innerBar
}
var bar = foo()
bar.setName('羊哥')
console.log(bar.getName());

首先分析下这串代码,在函数foo中创建了一个对象,在对象中构建两个函数并返回对象,构成闭包。然后再通过对象中的函数访问闭包。画一个图分析下

  • 这是在var bar = foo()执行之后的内存空间,foo函数被调用完后销毁,里面被调用的变量生成闭包,生成的对象放在堆中,地址返回给bar image.png
  • bar.setName('羊哥'),根据作用域链的关系找到闭包中的myname改为羊哥
  • console.log(bar.getName());输出闭包中的text1和myname

最后输出1,羊哥