【JS】JavaScript高级程序设计-第四章 变量、作用于和内存问题

四、变量、作用域和内存问题

上一章已经学习了基本数据类型 和部分引用类型,那么这些数据是怎么存储的呢? 作用域又是什么呢 接下来看看吧~

4.1 基本类型和引用类型的值

变量存储
基本类型的值:存在栈内存中的简单数据段,通过按值访问,可以直接操作保存在变量中实际的值。
引用类型的值:保存在堆内存中的对象(栈内存中存放该对象的内存地址),值大小不固定,不能直接操作对象的内存空间,因此引用类型的值按引用访问。
后面我在用图解修改一下吧 暂时不方便画图~
栈内存:

变量名
str‘123’
num123
arrarr内存地址
objobj 内存地址

变量复制
复制基本类型的值,则会在对象上上创建一个新值
var num1 = 5; var num2= num1;
此时num1 和num2 中保存的值都是5,只是这两个5是相互独立的,不会相互影响。
但复制引用类型的值时,同样会将值复制一份放到新变量分配的空间,但此时存储的是内存地址,也就是一个指针指向堆内存中的一个对象。即两个变量实际引用同一个对象,操作其中一个变量都会影响另一个 因此在复制引用类型时 通常我们使用深拷贝

传递参数
所有函数的参数都是按值传递,即将外部值复制给函数内部的参数。只不过这个值可能是复制基本数据类型,也可能是引用类型。
因此 传递基本类型,无法影响外部参数;而传递引用数据类型的值,函数内部改变会影响外部的值

// 函数addTen 接收一个参数num, 该参数实际是函数的局部变量
function addTen(num) {
  num += 10;
  return num;
}
var count = 20;
var result = addTen(count);
console.log(count); // 20
console.log(result); // 30
// 传递引用数据类型值
function setName(obj) {
  obj.name = 'zhangsan'
}
var person = new Object(); 
setName(person)
console.log(person.name); // 'zhangsan'

检测类型
第三章基础类型时提到使用typeof可以检测具体类型,但对引用类型没什么用;
引用类型检测可以使用instanceof

var person = new Object(); 
console.log(person instanceof Object); //true

4.2 执行环境及作用域

执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。
某个执行环境中所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境window对象直到应用程序退出才会被销毁)
作用域链: 当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。保证对执行环境有权访问的所有变量和函数的有序访问。

var color ='blue';
function change() {
  if(color === 'blue') {
    color = 'red'
  }
}
change();
console.log('color is', color);
// 函数change的作用域链包含两个对象:它自己的变量对象(内定义着arguments对象)+全局环境的变量对象
// 内部访问color变量过程: 1.change() 无 2.外部-此处即全局-找到了。 整个过程是向上搜索作用于链来查找的。
//(内部环境可以通过作用域链访问外部, 外部环境不可以访问内部环境中的任何变量和函数)

4.2.1 延长作用域链

某些语句通过在作用域链前端临时增加一个变量对象来延长作用域链。(代码执行后该变量对象被移除)
1.try-catch语句的catch块: 会创建一个新的变量对象,包含的是被抛出的错误对象的声明
2.with语句。会将制定对象添加到作用域链中。

4.2.2 没有块级作用域(这里指var)

块级作用域由最近的一对包含花括号{}界定
变量的作用域:
1.var : var声明的变量会自动被添加到最接近的环境中。即函数内部创建是函数的局部环境,width语句中是函数环境。若没有使用var声明变量,该变量会被自动添加到全局环境 在实际开发过程中切记不要不声明直接初始化,很可能会导致变量数据错误!(当然,在严格模式下,这样书写会报错)

if(true){
  var color ='blue'
}
console.log(color); //if语句中声明的变量会添加到当前的执行环境中 即输出blue
// *重点注意for语句,创建的变量i在循环结束后依然存在循环体外部的执行环境中!

var声明提升:var声明的变量会自动提升到函数作用域 顶部

function getColor() {
 console.log(color);
 var color = 'blue';
}
getColor(); // 会输出blue 

2.let: let声明的范围是块作用域, 即在块以外使用会报错(所以for声明变量i最好用let哦
)。同时let 也不允许同一个块作用域中出现冗余声明(var可以);不会在作用域中被提升

if(true){
  let color2 ='red'
}
console.log(color2); // 报错 color2 is not defined

if(true){
  let color2 ='red'
  let color2 ='red'; // 报错 SyntaxError: Identifier 'color2' has already been declared
}

if(true){
  console.log(color2); //报错  ReferenceError: Cannot access 'color2' before initialization
  let color2 ='red';
}

3.const:const 与 let基本相同,不同的是它声明变量时必须同时初始化变量,且不能修改。(注意:如果 const 变量引用的是一个对象, 那么修改这个对象内部的属性是可以的)

4.3 垃圾收集

js具有自动垃圾收集机制,即执行环境会负责管理代码执行过程中使用的内存。

4.3.1 标记清除

当变量进入环境时,就将这个变量标记为进入环境,当变量离开环境时,则标记为离开环境。(标记清除最常用的垃圾收集方式)
①垃圾收集器会在运行的时候会给存储在内存中的所有变量都加上标记。
②去掉「环境中的变量」或者「被进入环境中的变量所引用的变量」的标记。
③此后在被加上标记的变量则被视为准备删除的变
④后垃圾收集器完成内存清除工作,销毁带标记的值并回收他们所占用的内存空间。
存在问题: 清除后空闲内存空间是不连续的,导致内存碎片化;内存再分配速度慢

三种分配方式:
First-fit,找到大于等于 size 的块立即返回
Best-fit,遍历整个空闲列表,返回大于等于 size 的最小分块
Worst-fit,遍历整个空闲列表,找到最大的分块,然后切成两部分,一部分 size 大小,并将该部分返回

4.3.2引用计数

跟踪记录每个值被引用的次数。(低版本的IE使用这种方式)
①当声明了一个变量并将一个引用类型值赋值给该变量时,引用次数为1.
②如果同一个值又被赋值给另一个变量,则引用次数+1
③如果该变量的值被其他的值覆盖了,则引用次数-1
当引用次数变为0时就说明没办法访问这个值就可以回收占用的内存空间。
存在问题: 循环引用(对象A 包含一个指向对象B的指针,对象B包含一个指向对象A的引用)

// 此时objA objB 引用次数为2,若采用引用计数执行完毕后 他objA objB会依然存在
function test () {
  var objA = new Objeat();
  var objB = new Object();
  objA.oneObj = objectB;
  objB.anthoerObj = objectA;
}

4.3.3性能问题

垃圾收集器是周期性运行的,因此确定垃圾收集的时间间隔是一个非常重要的问题。
IE7,javaScript引擎的垃圾收集例程改变了工作方式:触发垃圾手机的变量分配、字面量和数组元素的临界值被调整为动态修正(原来是256个变量、4096个对象/数组字面量、64kb的字符串)。如果垃圾收集例程回收的内存分配量低于15%,则变量、字面量和数组元素的临界值就会加倍。

4.3.4 管理内存

优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将值设置为null来释放其引用(解除引用)。接触引用不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。

总结

1.基本类型值存储在栈内存中,占据固定大小;引用类型存储在堆内存中,栈中保存的是对象的引用
2.引用类型使用instanceof判断,typeof用来判断基本类型
3.var 函数作用域; let块级作用域
4.函数的局部环境可以通过作用域链访问父环境、全局环境;全局环境不能直接访问局部环境中的数据;
5.js是具有自动垃圾收集机制的编程语言。

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值