当前位置: 首页 > news >正文

第4章 变量、作用域与内存

引言


由于js是一门只有在声明变量后才能明确类型的语言,并且在任意时刻都可以改变数据类型。这也引起了一些问题


原始值与引用值

原始值就是基本数据类型,引言值就是复杂数据类型
变量在赋值的时候。js会判断如果是原始值,访问时就是按值访问。如果是引用值,访问就是按引用访问

在很多语言中,字符串是使用对象表示的,因此被认为是引用类型。ECMAScript打破了这个惯例。

动态属性

原始值不能动态的crud属性和方法

let name = "Nicholas";
name.age = 27;
console.log(name.age);   // undefined

引用值可以动态的crud属性和方法

let person = new Object();
person.name = "Nicholas";
console.log(person.name); // "Nicholas"

原始类型如果通过new关键字赋值,本质上还是Object类型

let name = new String("Matt");
name.age = 26;console.log(name.age);     // 26
console.log(typeof name); // object

复制值

原始值之间的赋值是单纯的复制,俩个互不干扰

int num1=5;
int num2=num1;

在这里插入图片描述

引用值之间的赋值是地址的复制,地址指向同一片内存空间。操作的也是同一片内存空间

 let obj1 = new Object();obj1.name = "Nicholas";let obj2 = obj1;

在这里插入图片描述

传递参数

ECMAScript中所有函数的参数都是按值传递的。js的函数参数当接到传递过来的值后,会开辟一个空间将值赋值给函数参数

改变外部的person可能会错误的认为这是按引用传值,其实这是错误的理解

    function setName(obj) {obj.name = "Nicholas";}let person = new Object();setName(person);console.log(person.name);   // "Nicholas"

如果是引用传值,那么person.name应该是Greg。

 function setName(obj) {obj.name = "Nicholas";obj=newObject();obj.name="Greg";}let person = new Object();setName(person);console.log(person.name);   // "Nicholas"

确定类型

typdeof对原始类型非常有用,但是我们想知道一个引用类型的具体类型。typdeof就无能无力了

所有变量和Object匹配都会返回true,因为所有引用类型都是用Object实现的。
如果用instanceof检测原始值,则始终会返回false,因为原始值不是对象。

console.log(person instanceof Object);   // 变量person是Object吗?
console.log(colors instanceof Array);    // 变量colors是Array吗?
console.log(pattern instanceof RegExp); // 变量pattern是RegExp吗?

执行上下文与作用域

变量或函数的上下文决定了它们可以访问哪些数据,以及它们的行为

全局上下文是最外层的上下文,在浏览器中,全局上下文就是我们常说的window对象,因此所有通过var定义的全局变量和函数都会成为window对象的属性和方法。全局上下文在应用程序退出前才会被销毁

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

搜索过程始终从作用域链的最前端开始,然后逐级往后,直到找到标识符。(如果没有找到标识符,那么通常会报错。)

每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中去搜索。

变量声明

1. 使用var的函数作用域声明

在使用var声明变量时,变量会被自动添加到最接近的上下文

如果变量未经声明就被初始化了,那么它就会自动被添加到全局上下文(在调用函数后会被创建)

function add(num1, num2) {var sum = num1 + num2;return sum;
}
let result = add(10, 20); // 30
console.log(sum);           // 报错:sum在这里不是有效变量

var声明会被拿到函数或全局作用域的顶部,位于作用域中所有代码之前

提升让同一作用域中的代码不必考虑变量是否已经声明就可以直接使用(不要使用

console.log(name); // undefined
var name = 'Jake';
function() {console.log(name); // undefinedvar name = 'Jake';
}

2. 使用let的块级作用域声明

let和var最大的区别是作用域是块级的,换句话说,if块、while块、function块,甚至连单独的块也是let声明变量的作用域

if (true) {let a;
}
console.log(a); // ReferenceError: a没有定义
while (true) {let b;
}
console.log(b); // ReferenceError: b没有定义
function foo() {let c;
}
console.log(c); // ReferenceError: c没有定义// 这没什么可奇怪的// var声明也会导致报错
// 这不是对象字面量,而是一个独立的块
// JavaScript解释器会根据其中内容识别出它来
{let d;
}
console.log(d); // ReferenceError: d没有定义

let与var的另一个不同之处是在同一作用域内不能声明两次

let的行为非常适合在循环中声明迭代变量。使用var声明的迭代变量会泄漏到循环外部,这种情况应该避免

3. 使用const的常量声明

使用const声明的变量必须同时初始化为某个值。一经声明,在其生命周期的任何时候都不能再重新赋予新值。

const a; // SyntaxError:常量声明时没有初始化
const b = 3;
console.log(b); // 3
b = 4; // TypeError: 给常量赋值

const除了要遵循以上规则,其他方面与let声明是一样的:

赋值为对象的const变量不能再被重新赋值为其他引用值,但对象的键则不受限制。

const o1 = {};
o1 = {}; // TypeError:给常量赋值
const o2 = {};
o2.name = 'Jake';
console.log(o2.name); // 'Jake'

垃圾回收

JavaScript是通过自动内存管理实现内存分配和闲置资源的语言。确定哪个变量不会再使用,然后释放它占用的内存。这个过程是周期性的,即垃圾回收程序每隔一定时间就会自动运行

标记清理

从根上下文(通常是全局上下文或当前执行的函数)开始,遍历所有的变量和对象。将所有可达(被引用)的对象标记为“存活”。在标记阶段之后,遍历整个内存空间,将未标记为“存活”的对象判定为垃圾,进行清除。这些未标记的对象会被销毁,其占用的内存将被释放。

引用计数

对每个值都记录它被引用的次数。声明变量并给它赋一个引用值时,这个值的引用数为1。如果同一个值又被赋给另一个变量,那么引用数加1,如果保存对该值引用的变量被其他值给覆盖了,那么引用数减1。当一个值的引用数为0时,就说明没办法再访问到这个值了,因此可以安全地收回其内存了。垃圾回收程序下次运行的时候就会释放引用数为0的值的内存。

这种方法没有办法解决循环引用的问题,即俩个对象互相引用

function problem() {let objectA = new Object();let objectB = new Object();objectA.someOtherObject = objectB;objectB.anotherObject = objectA;
}

内存管理

将内存占用量保持在一个较小的值可以让页面性能更好。优化内存占用的最佳手段就是保证在执行代码时只保存必要的数据。

解除引用。这个建议最适合全局变量和全局对象的属性。局部变量在超出作用域后会被自动解除引用

function createPerson(name){let localPerson = new Object();localPerson.name = name;return localPerson;
}
let globalPerson = createPerson("Nicholas");
// 解除globalPerson对值的引用,
globalPerson = null;

解除引用的关键在于确保相关的值已经不在上下文里了,因此它在下次垃圾回收时会被回收。

1. 通过const和let声明提升性能

由于const和let的块级作用域,让他们可能更早被垃圾回收

2. 隐藏类和删除操作

js引擎V8会在后台配置,让这两个类实例共享相同的隐藏类,因为这两个实例共享同一个构造函数和原型

function Article() {this.title = 'Inauguration Ceremony Features Kazoo Band';
}
let a1 = new Article();
let a2 = new Article();

如果在后面添加如下代码后,此时两个Article实例就会对应两个不同的隐藏类。根据这种操作的频率和隐藏类的大小,这有可能对性能产生明显影响。

a2.author = 'Jake';

为了避免性能的消耗,最好的法子就在构造函数中一次性声明完毕

function Article(opt_author) {this.title = 'Inauguration Ceremony Features Kazoo Band';this.author = opt_author;
}
let a1 = new Article();
let a2 = new Article('Jake');

在代码结束后,即使两个实例使用了同一个构造函数,它们也不再共享一个隐藏类。动态删除属性与动态添加属性导致的后果一样

function Article() {this.title = 'Inauguration Ceremony Features Kazoo Band';this.author = 'Jake';
}
let a1 = new Article();
let a2 = new Article();
delete a1.author;

最佳实践是把不想要的属性设置为null。这样可以保持隐藏类不变和继续共享,同时也能达到删除引用值供垃圾回收程序回收的效果。

function Article() {this.title = 'Inauguration Ceremony Features Kazoo Band';this.author = 'Jake';
}
let a1 = new Article();
let a2 = new Article();
a1.author = null;

3. 内存泄漏

在内存有限的设备上,或者在函数会被调用很多次的情况下,内存泄漏可能是个大问题。

最常见的内存泄露,方法被调用后会在window上挂载一个name属性。只要window不被清除,那么这个属性就一直存储。使用let,const可以避免这种情况的发生

function setName() {name = 'Jake';
}

由于定时器的回调函数一直调用name,所以name永远不会被清除

let name = 'Jake';
setInterval(() => {console.log(name);
}, 100);

js中的闭包很容易造成内存泄露的问题,调用outer()会导致分配给name的内存被泄漏。以上代码执行后创建了一个内部闭包,只要返回的函数存在就不能清理name,因为闭包一直在引用着它

let outer = function() {let name = 'Jake';return function() {return name;};
};

相关文章:

第4章 变量、作用域与内存

引言 由于js是一门只有在声明变量后才能明确类型的语言,并且在任意时刻都可以改变数据类型。这也引起了一些问题 原始值与引用值 原始值就是基本数据类型,引言值就是复杂数据类型 变量在赋值的时候。js会判断如果是原始值,访问时就是按值访问…...

Python爬虫遇到重定向问题解决办法汇总

在进行Python爬虫任务时,遇到重定向问题是常见的问题之一。重定向是指在发送请求时,服务器会返回一个新的URL,将请求重新定向到该URL。为了帮助您解决这个问题,本文将提供一些实用的解决办法,并给出相关的代码示例&…...

R并行计算

1-lapply()函数介绍: 为什么介绍这个函数呢?因为在windows中使用parLapply()函数和lapply()的结构和用法是非常相似的,我们只需要将原本用lapply(x, fun)迭代函数 直接改写成 parLapply(makeCluster(c1), x, fun)即可,这里的直接…...

STM32 低功耗-待机模式

STM32 待机模式 文章目录 STM32 待机模式第1章 低功耗模式简介第2章 待机模式简介2.1 进入待机模式2.1 退出待机模式 第3章 待机模式代码部分总结 第1章 低功耗模式简介 在 STM32 的正常工作中,具有四种工作模式:运行、睡眠、停止和待机模式。 在系统或…...

极海APM32F003F6P6烧写问题解决记录

工作中遇到的,折腾了好久,因为电脑重装过一遍系统,软件也都重新安装了,所以不知道之前的配置是什么,旧项目代码编译没问题,烧写时疯狂报错,用的是JLink。 keil版本v5.14 win10版本 JLink版本…...

【大数据】Flink 详解(一):基础篇

Flink 详解(一):基础篇 1、什么是 Flink ? Flink 是一个以 流 为核心的高可用、高性能的分布式计算引擎。具备 流批一体,高吞吐、低延迟,容错能力,大规模复杂计算等特点,在数据流上提…...

ChatGPT 作为 Python 编程助手

推荐:使用 NSDT场景编辑器 助你快速搭建可编辑的3D应用场景 简单的数据处理脚本 我认为一个好的起点是某种数据处理脚本。由于我打算让 ChatGPT 之后使用各种 Python 库编写一些机器学习脚本,这似乎是一个合理的起点。 目标 首先,我想尝试…...

饿了么输入框限制只能输入数字,并且保留小数

可以使用饿了么ui中的input-number组件实现输入框只能输入数字,这样就不能输入数字以外的,controls隐藏输入框左右俩边的加减按钮,precision小数点保留多少位,2则是俩位,但是会导致默认值为0.00的情况,俩种…...

kylin-Desktop gsettings 获取或设置系统配置

gsettings提供了对GSetings的命令行操作。GSetings实际上是一套高级API,用来操作dconf。 dconf存储着GNOME3的配置,是二进制格式。它做为GSettings的后端系统存在,暴露出低级API。在GNOME2时代,类似的角色是gconf,但它是以XML文本形式存储。 更接地气的说法是,dconf是G…...

setmap使用

目录 set使用 set的模板参数 构造函数 成员函数 insert iterator ​编辑 find count pair pair 的模板参数 make_pair multiset使用 multiset 的模板参数 set 与 multiset 的区别 count map使用 map 的模板参数 构造函数 insert iterator find ​编辑 cou…...

Python3 网络爬虫开发实战

JavaScript逆向爬虫 JavaScript接口加密技术,JavaScript有以下两个特点: JS代码运行在客户端,所以它必须在用户浏览器加载并运行JS代码公开透明,所以浏览器可以直接获取到正在运行的JS源码。 所以JS代码不安全,任何…...

docker: CMD和ENTRYPOINT的区别

ENTRYPOINT: 容器的执行命令(属于正统命令) 可以使用--build-arg ENVIROMENTintegration参数覆盖 ocker build --build-arg ENVIROMENTintegration 两者同时存在时 CMD作为ENTRYPOINT的默认参数使用外部提供参数会覆盖CMD提供的参数。 CMD单…...

DC电源模块对于定制的要求主要有这几点

BOSHIDA DC电源模块对于定制的要求主要有这几点 DC电源模块是一种将交流电转换成为稳定的直流电的装置。在现代工业生产中,DC电源模块被广泛应用于各种电子设备中,例如计算机、手机、电视等。为了满足不同用户需求,DC电源模块的定制需求也是…...

Kubernetes高可用集群二进制部署(六)Kubernetes集群节点添加

Kubernetes概述 使用kubeadm快速部署一个k8s集群 Kubernetes高可用集群二进制部署(一)主机准备和负载均衡器安装 Kubernetes高可用集群二进制部署(二)ETCD集群部署 Kubernetes高可用集群二进制部署(三)部署…...

网关 GateWay 的使用详解、路由、过滤器、跨域配置

一、网关的基本概念 SpringCloudGateway网关是所有微服务的统一入口。 1.1 它的主要作用是: 反向代理(请求的转发) 路由和负载均衡 身份认证和权限控制 对请求限流 1.2 相比于Zuul的优势: SpringCloudGateway基于Spring5中…...

vsocde里面远程连接服务器报could not esatablish connection xxxx

我在vscode里面远程连接服务器编辑代码时,正常我按F1选择了服务器IP地址,然后让我选在Linux,然后我再输入服务器密码,但是当我选择Linux系统之后直接没出让我输入服务器密码的输入框,而是直接报错 could not esatablis…...

Hi3798MV200 恩兔N2 NS-1 (二): HiNAS海纳思使用和修改

目录 Hi3798MV200 恩兔N2 NS-1 (一): 设备介绍和刷机说明Hi3798MV200 恩兔N2 NS-1 (二): HiNAS海纳思使用和修改Hi3798MV200 恩兔N2 NS-1 (三): 制作 Ubuntu rootfsHi3798MV200 恩兔N2 NS-1 (四): 制作 Debian rootfs 关于 海纳思全称是海思机顶盒NAS系统, 网站 https://www…...

无涯教程-Perl - foreach 语句函数

foreach 循环遍历列表值,并将控制变量(var)依次设置为列表的每个元素- foreach - 语法 Perl编程语言中的 foreach 循环的语法是- foreach var (list) { ... } foreach - 流程图 foreach - 示例 #!/usr/local/bin/perllist(2, 20, 30, 40, 50);# foreach loop ex…...

easyWechat 5.x 复写代码 获取企业微信授权用户敏感信息

复写 (new SocialiteManager($config))->extend(wework, function ($config) {return new \App\Extend\EasyWechat\Work\WeWork($config);});创建的 \App\Extend\EasyWechat\Work\WeWork是我们需要复写的类 <?phpnamespace App\Extend\EasyWechat\Work;use Overtrue\So…...

医疗器械研发中的可用性工程实践(一)

致读者&#xff1a;以前看《楚门的世界》&#xff0c;《蝴蝶效应》&#xff0c;《肖申克的救赎》&#xff0c;《教父》&#xff0c;《横道世之介》&#xff0c;《老友记》&#xff0c;一个人的一生匆匆。作为平凡人就是历史大河中的浪花&#xff0c;顺势而为&#xff0c;起起伏…...

Ostrakon-VL-8B图文对话实战:上传厨房照片→提问卫生问题→获取结构化反馈

Ostrakon-VL-8B图文对话实战&#xff1a;上传厨房照片→提问卫生问题→获取结构化反馈 想象一下&#xff0c;你是一家连锁餐厅的卫生督导员&#xff0c;每周要巡查几十家门店的厨房。传统方式是什么&#xff1f;拿着检查表&#xff0c;挨个角落拍照&#xff0c;回到办公室再整…...

别再被JJWT新版坑了!手把手教你从0.12.x降级到0.11.2解决parseClaimsJws报错

JJWT版本降级实战&#xff1a;从0.12.x回退0.11.2解决parseClaimsJws报错指南 最近在Spring Boot项目中整合JWT时&#xff0c;不少开发者反馈升级到JJWT 0.12.x后突然遭遇parseClaimsJws方法消失的编译错误。这个看似简单的API变动背后&#xff0c;其实是JJWT团队对安全架构的重…...

OpenClaw会议纪要自动化:Qwen3.5-9B实时转录与待办项提取

OpenClaw会议纪要自动化&#xff1a;Qwen3.5-9B实时转录与待办项提取 1. 为什么需要会议纪要自动化 每周三的团队例会总是让我头疼——90分钟的会议结束后&#xff0c;我需要花40分钟整理录音、标记关键决议、分配待办事项。直到上个月用OpenClawQwen3.5-9B搭建了自动化流程&…...

如何为 3D 轮播文本添加可点击的 URL 链接

...

Go的strings.Builder:高性能字符串拼接的秘密

Go的strings.Builder&#xff1a;高性能字符串拼接的秘密 在编程中&#xff0c;字符串拼接是常见的操作&#xff0c;但不同的实现方式性能差异巨大。Go语言中的strings.Builder提供了一种高效、灵活的字符串拼接方案&#xff0c;尤其适合处理大量字符串拼接的场景。本文将深入…...

嵌入式通信协议设计的7项核心原则与实战优化

1. 嵌入式通信协议设计核心原则在嵌入式系统开发中&#xff0c;设备与PC间的通信协议设计直接影响着整个系统的可靠性、可维护性和扩展性。经过多年实战&#xff0c;我总结了七项关键设计原则&#xff0c;这些原则在资源受限的嵌入式环境中尤为重要。1.1 简单性优先原则固定长度…...

云原生应用的性能测试与优化

云原生应用的性能测试与优化 &#x1f525; 硬核开场 各位技术老铁&#xff0c;今天咱们聊聊云原生应用的性能测试与优化。别跟我扯那些理论&#xff0c;直接上干货&#xff01;在云原生时代&#xff0c;性能是用户体验的关键&#xff0c;也是系统可靠性的保障。不搞性能测试与…...

Harness工程可视化入门基础教程(非常详细),拿捏Vibe Coding看这篇就够了!

在最新的 Routa Desktop 中&#xff0c;我们引入了 Harness 工程可视化系统。它并不是一个展示“AI 写了多少代码”的界面&#xff0c;也不是为了给生成式开发增加一层炫目的仪表盘&#xff0c; 而是试图回答一个更关键的问题&#xff1a; 当 AI 逐渐成为软件交付链路中的执行者…...

C++笔记 Lambda表达式

Lambda表达式是C11引入的核心特性之一&#xff0c;本质是一种匿名函数&#xff0c;可以捕获作用域内的变量&#xff0c;无需单独定义函数名&#xff0c;就能实现简洁、灵活的代码编写&#xff0c;尤其适合作为回调函数、算法参数&#xff08;如STL算法&#xff09;等场景&#…...

向量运算的几何奥秘:叉积与点积的混合运算规则解析

1. 从几何视角理解向量运算的本质 第一次接触向量运算时&#xff0c;很多人会被各种公式绕得头晕。其实换个角度看&#xff0c;这些运算规则都对应着直观的几何现象。就像小时候玩积木&#xff0c;看似简单的拼接背后藏着空间结构的奥秘。 点积像是测量两个向量的"重合度&…...