Javascript 进阶|变量、执行环境、变量对象、作用域链

Javascript 进阶|变量、执行环境、变量对象、作用域链

来看看 Javascript 的:

  • 基础数据类型与引用数据类型
  • 变量与复制变量值
  • 执行环境
  • 变量对象与活动对象
  • 作用域链
  • 内存空间管理
  • 垃圾回收机制
基础数据类型与引用数据类型

而在 Javascript 中有两种类型:基础数据类型与引用数据类型

有 5 种简单数据类型 (也称为基本数据类型):Undefined、Null、Boolean、Number 和 String。

还有 1 种复杂数据类型——Object

变量与复制变量值

Javascript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说, 每个变量仅仅是一个用于保存值的占位符而已。定义变量时要使用 var 操作符 (注意 var 是一个关键 字),后跟变量名 (即一个标识符),如下所示:
var message;

变量可能包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是 简单的数据段,而引用类型值指那些可能由多个值构成的对象。

他们的处理方式不一样的, 引用类型的值是保存在内存中的对象。与其他语言不同,JavaScript 不允许直接访问内存中的位置, 也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。 为此,引用类型的值是按引用访问的。

在这里, 我有一个比喻,

  • 变量可以说是一个盒子;
  • 基础数据类型的值是一些小饰品;
  • 对象的值是一个大房子

可以想象 , 盒子存得下一些小饰品,但是存不下一个房子,但是可以存房子的地址

如果这样比喻,复制变量值就比较容易了解, 复制是把变量当前的值赋值给新的变量,而基础数据类型的值是一些小饰品 , 赋值过后的值改变了,不会影响到 b; 存放对象的值的变量是该对象的引用(房子的地址), 如果 b 改变了, a 一样变化,约等于房子装修了,但是地址没有改

var a = 1
var b = a
b=2
console.log(a == b) //false

var obj1={a:1}
var obj2=obj1
obj2.b = 2
console.log(obj1 == obj2) //true

传递参数,其实就可以被当做是一个复制变量值的过程,如果复制的是基础类型,那函数里面的参数值变化不会影响到外层变量的变化;当如果复制的是引用类型(地址),那它的值如果变化也会引起外层变量的对象值变化,当然,如果把该参数的变量重新赋值,那它就跟外面的变量没有关系了。

var a1 = 1
var obj2 = {b:1}
var obj3 = {b:1}
function f1 (p1,p2,p3){
    p1 = 2
    console.log(a1,",",p1)  // 1,2

    p2.c = 1
    console.log(obj2 == p2) //true

    p3 ={b:1}
    console.log(obj3 == p3) //false

}
f1(a1,obj2,obj3)
执行环境

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象 (variable object),环境中定义的所有变量和函数都保存在这个对象中。

每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。 而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。

这里提到的环境栈就是内存栈 , 所以我想可以这样说, 每个执行环境其实就是一个内存区域,而当 javascript 启动时,就会启动第一个全局的执行环境 (内存区域), 当调用一个函数(函数 1)时,就会开辟第二个执行环境 (内存区域):

  • 如果函数 1 没有调用其他函数,函数执行完时,就会把该内存区域释放;
  • 如果函数 1 再调用一个函数(函数 2),就会开辟第三个执行环境 (内存区域);函数 2 执行完释放该内存区域 , 函数 1 释放该内存区域;

这就是一个环境(内存)栈 , 最后面被调用的函数总是在最外面层,执行完就会被释放。

变量对象与活动对象

每个函数都有自己的执行环境,每个函数里面都有很多定义的变量或者函数,所以在每个执行环境都会有其变量对象存放该函数下的变量或者函数(javascript 的引擎可以识别该变量是不是被使用,如果未必使用的变量时不会放在该执行环境的的变量对象),

在环境(内存)栈 , 最后面的执行环境是当前正在被调用的函数,而当前正在被调用的函数(执行环境)的变量对象就是活动对象

作用域链

在活动对象 (正在被调用的函数) 中,使用的变量,有机会是在当前活动对象找不到 , 但是它可能在环境(内存)栈的上一个执行环境 , 同样可以获取该变量的值 , 这个是通过作用域链完成的

function f1() {
    var v1 = 1
    function f2() {
        var v2 = 2
        console.log(v1, ",", v2) // 1,2, 上一层
    }
    f2()
}
f1()

当代码在一个环境中执行时,会创建变量对象的一个作用域链 (scope chain)。作用域链的用途,是 保证对执行环境有权访问的所有变量和函数的有序访问

变量的查找是沿着作用域链一级一级地搜索变量的过程。搜索过程始终从作用域链的前端开始, 然后逐级地向后回溯,直至找到该变量为止, 如果找不到就 ReferenceError 。

内存空间管理

JavaScript 的内存生命周期

  1. 分配你所需要的内存
  2. 使用分配到的内存(读、写)
  3. 不需要时将其释放、归还
    为了便于理解,我们使用一个简单的例子来解释这个周期。
var a = 20;  // 在内存中给数值变量分配空间
alert(a + 100);  // 使用内存
a = null; // 使用完毕之后,释放内存空间
)
垃圾回收机制

JavaScript 有自动垃圾收集机制,那么这个自动垃圾收集机制的原理是什么呢?其实很简单,就是找出那些不再继续使用的值,然后释放其占用的内存。垃圾收集器会每隔固定的时间段就执行一次释放操作。

在 JavaScript 中,最常用的是通过标记清除的算法来找到哪些对象是不再继续使用的,因此 a = null 其实仅仅只是做了一个释放引用的操作,让 a 原本对应的值失去引用,脱离执行环境,这个值会在下一次垃圾收集器执行操作时被找到并释放。而在适当的时候解除引用,是为页面获得更好性能的一个重要方式。

这里也解释了,为什么没有被使用的变量不会放在环境变量的变量对象中,因为他已经被当为垃圾所清除。

总结

执行环境、变量对象、活动对象、作用域、变量,还是用变量是一个盒子,对象是一个房子的做比喻:
假设我在银河小区 A 栋 2 楼 1 号房

  • 执行环境可以是: 银河小区、A 栋、2 楼、1 号房
  • 变量对象:
  • 银河小区的变量对象:A 栋、B 栋、公关盒子……
  • A 栋的变量对象:1 楼、2 楼、A 栋的公共盒子……
    ……
  • 活动对象:1 号房的变量对象 (1 号房的盒子)
  • 作用域链: 1 号房 ->2 楼 -> A 栋 -> 银河小区
参考

https://segmentfault.com/a/1190000012646195
《Javascript 高级程序设计(第 3 版)》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值