js变量、作用域和内存
一、变量
1.分类:
变量可以分为基本类型值和引用类型值,像在之前的随笔中介绍的数值,字符串类型这些就是属于基本类型值。而引用类型值则是指对象。然而,引用类型的值是保存在内存中的对象,js和其他编程语言不同的是,我们不能够直接操作内存中的对象,只能通过当中的引用来操作。所以引用类型的值是按引用访问的。
2.保存方式:
基本类型值和引用类型值的保存方式是不一样的,举个例子:
1 2 3 4 | var person= new object(); person.name= "Nicholas" ; person.age=27; alert(person.age); //"Nicholas" |
以上例子创建了一个对象,并给该对象添加了一个name属性且赋值Nicholas,这个属性只要对象一直存在就也一样一直存在
1 2 3 | var name= "Nicholas" ; name.age=27; alert(name.age); //undefined |
而这个例子则是给基本变量添加了一个age属性,虽然js不会报错,但是该属性是不存在的
3.复制变量值
(1)对于基本类型值,一个变量向另一个变量赋值的时候,会在变量对象上创建一个新值,然后把该值复制到新变量分配的位置上,所以,新变量与原先变量两者是完全独立的。
(2)对于引用类型值,一个变量向另一个变量复制的时候,则是将指针复制过去,因此,两个变量实际上将引用同一个对象,改变其中一个对象,就会影响另一个对象。
4.传递参数
js中参数都是按值来传递的,也就是说,被传递的值如果是基本类型的值,该值将会被复制给一个局部变量。而在传递引用类型的值的时候,会把这个值在内存中的地址传递给一个变量,所以这个局部变量的变化会反映在函数的外部。例如:
1 2 3 4 5 6 7 8 | //基本变量<br>function addTen(num){ num+=10; return num; } var count =20; var result=addTen(count); alert(count); //20,没有变化 alert(result); //30<br><br>//对象<br>function setName(obj){<br>obj.name="Nicholas";<br>}<br>var person=new object();<br>setName(person);<br>alert(person.name);//"Nicholas" |
*因为在局部作用域中修改的对象会在全局作用域中反映出来,所以很多开发人员认为对象是按引用传递的。为了证明是按值传递,我们来看一个例子:
1 2 3 4 5 6 7 8 9 | function setName(obj){ obj.name= "Nicholas" ; obj= new object(); obj.name= "Greg" ; } var person= new Object(); setName(person); alert(person.name); //"Nicholas" |
*因为当函数内部重写obj时,这个变量引用的就是一个局部对象了,所以这个局部对象在函数执行完毕后立即被销毁。因此,在弹出对话框时的内容依旧是Nicholas。故而,参数是按值传递的,如果是按引用来传递的话,obj的name属性的值将会被改变。
5.检测类型
前面我们接触过的检测类型是typeof操作符,但是这只适用于基本类型的变量,对于是对象的变量,我们应当使用instanceof。语法如下:
1 | result=variable instanceof constructor; |
例如:
1 2 3 | alert(person instanceof Object); //变量person是 Object吗? alert(colors instanceof Array); //变量colors是 Array吗? alert(pattern instanceof RegExp); //变量pattern是RegExp吗? |
该操作符将返回的是布尔值
二、执行环境和作用域
执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。执行环境分为全局执行环境和局部执行环境。在web浏览器中,全局执行环境被认为是windows对象,因此所有的全局变量和函数都是作为window对象的属性和方法创建的。全局执行环境直到应用程序退出后(例如关闭网页或浏览器)时才会被销毁,保存在其中的变量和函数定义也随之销毁。
作用域链是用于保证 对执行环境有权访问的所有变量和函数的有序访问,作用域链的前端,始终都是当前执行的代码所在的变量对象。如果这个环境是函数,那么这个函数就是最前的变量对象,往后就是包含在外的一个函数,或是外部变量,一次类推。
*我们的标识符解析都是沿着作用域链一级级地搜索标识符的过程,所以,全局环境中不可以调用局部环境中的变量。但是局部环境却可以使用全局变量。
(1)延长作用域链
有两个方法可以帮助我们延长作用域链:try-catch语句的catch块以及with语句,我们下面重点来了解一下with语句:
1 2 3 4 5 6 7 | function buildUrl(){ var qs= "?debug=true" ; with (location){ var url=href+qs; } return url; } |
with语句括号内是location对象,当在语句块中引用href的时候,实际上引用的是location.href。with语句使得location的作用域延长到了作用域链的前端。with 语句可以方便地用来引用某个特定对象中已有的属性,去除多次书写对象名的麻烦。但是不能用来给对象添加属性。要给对象创建新的属性,必须明确地引用该对象。
(2)没有块级作用域链
在作用域方面,js没有块级作用域链,这和C语言等常见的语言很不一样。js只有全局和局部两种作用域。
1 2 3 4 | if ( true ){ var color= "blue" ; } alert(color); //blue |
在c语言等编程语言当中,因为存在块级作用域,所以color在花括号结束时将被销毁,所以将无法输出color。但是上面例子的代码却能正常输出,这就是因为js把color变量放在了全局环境当中去。所以,color没有被销毁。
*因为js的这个特点,所以在使用 for语句的时候要格外注意:
1 2 3 4 | for ( var i=0;i<10;i++){ doSomething(i); } alert(i); //10 |
这个时候,输出的i是第十次循环时的i变量
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· 语音处理 开源项目 EchoSharp
· 《HelloGitHub》第 106 期
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 使用 Dify + LLM 构建精确任务处理应用