JavaScript的变量、作用域与内存

本文详细探讨了JavaScript中的变量类型,包括原始值与引用值的复制和传递,以及如何确定变量类型。接着,文章深入讲解了执行上下文与作用域的概念,如作用域链、变量声明(var、let、const)以及作用域链的增强。最后,讨论了JavaScript的垃圾回收机制,如标记清理和引用计数,并提到了内存管理的最佳实践。
摘要由CSDN通过智能技术生成

1. 原始值与引用值

JavaScript变量可以包含两种不同类型的数据:原始值(Number,String,Boolean,Symbol,Null,Undefined)和引用值(Object)。

原始值变量按值访问,我们操作的是储存在变量中的实际值;引用值按引用访问,是保存在内存中的对象的引用。

1.1 复制值

原始值类型的变量在复制时是拷贝值,两个变量没有任何关系;

引用值类型的变量在复制时也是拷贝值,但是因为值是一个指针,所以两个变量会指向同一个对象,在一个变量上操作可能会影响到另一个变量;

1.2 传递参数

JavaScript中函数的参数都是按值传递的,函数外的值会被复制到函数内部的参数中,就像是复制值一样,包括原始类型和引用类型。

1.3 确定类型

原始值类型:typeof

引用值类型:variable instanceof constructor;instanceof用于原始值的时候衡为false

 

2. 执行上下文与作用域

执行上下文

变量或函数的执行上下文(简称“上下文”)决定了它们可以访问哪些数据以及它们的行为。

每个上下文都有一个关联的变量对象,这个上下文中定义的所有变量和函数都存在这个对象上;虽然无法用代码直接访问这个对象,但是后台处理数据会用到它。

全局上下文是最外层的上下文,在浏览器宿主环境中,全局上下文是window对象,所有通过var定义的全局变量和函数都会成为window对象的属性和方法。

上下文在其所有代码被执行完后会被销毁,包括定义在它上面的变量和函数。

每个函数调用都有自己的上下文,当代码执行流程进入函数时,函数的上下文会被压入到上下文栈中,当函数执行完,上下文栈会弹出该函数的上下文,将控制权返还给之前的上下文。

作用域链

上下文中的代码在执行的时候,会创建变量对象的一个作用域链,这个作用域链决定了代码在访问变量和函数时的顺序。

代码正在执行的上下文的变量对象始终位于作用域链的最前端,如果上下文是函数,那么其活动对象用作变量对象,活动对象上最开始只有一个变量arguments;作用域链的下一个变量对象来自包含上下文,在下一个来自下一个包含上下文,直到全局上下文。

代码执行时的标识符解析沿着作用域链逐级往后,内部的上下文可以通过作用域链访问外部上下文中的一切,外部的不能访问内部的。

 

2.1 作用域链增强

某些语句会在作用域链的前端添加一个上下文,这个上下文会在代码执行结束后被删除。

  • with语句
  • try/catch的catch块

 

2.2 变量声明

使用var的函数作用域声明

使用var声明变量时,变量会被自动添加到最近的上下文,在函数中最近的就是函数上下文(with语句中最近的也是函数上下文);如果变量没有被声明就直接初始化,就会被添加到全局上下文;

var声明会被拿到函数或全局上下文的顶部,这个现象就是变量提升(hoisting);

 

使用let/const的块级作用域声明

块级作用域由最近的一个{}界定;

在同一个块中不能重复使用let声明同一个变量;

严格来讲,let/const也会被变量提升,但是会出现“暂时性死区”,导致不能在声明前使用变量,但是内部变量的存在会屏蔽对外层上下文的访问;

var value = 20;
(function () {
  console.log(name);		// undefined
  console.log(value);		// Uncaught ReferenceError: Cannot access 'value' before initialization
  var name = 'local value';
  let value = 21;
})();

用let声明的变量value之所以会报错,是因为let/const造成的“暂时性死区”的问题;用let/const声明的变量,在包含它们的上下文被创建时就会被创建,但是,只有在变量的词法绑定已经被求值运算后(赋值),才能够被访问;所以严格来讲,let/const也会和var一样被变量提升,在赋值语句之前访问会报错;

在本题中,由于let value = 21;一句,在function的上下文中,value已经在上下文创建之初存在了,所以它的存在屏蔽了对外层value的访问,然而,由于let的暂时性死区的限制,它虽然存在但是无法被访问,所以这里会报错;
 

用const声明的变量在声明时就必须被初始化,原始类型的const变量不能修改;引用类型的const变量只是不能修改引用值,被引用的对象是可以修改内容的;

如果想整个对象都不能被修改,可以使用obj2 = Object.freeze(obj1)

 

3. 垃圾回收

垃圾回收机制有:

  • 标记清理
  • 引用计数

引用计数会遇到循环引用的问题;

垃圾回收程序周期性运行,如果运行过于频繁会导致性能严重下降;

内存管理

将内存占用量保存在一个较小的值可以让页面的性能更好,为此,在代码执行时尽量只保存必要的数据,如果数据不再需要,就把它置为null从而释放其引用(引用解除);

减少对象的先创建再补充的动态赋值,在构造函数里一次性声明所有属性,这样可以减少隐藏类的创建;

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值