整理 JS变量作用域

本文深入探讨JavaScript中的作用域规则、变量作用域、原型属性与实例属性的区别,以及不同类型的属性查找顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1  js静态属性和实例属性

   原型属性在实例化之后作为类的实例属性。但是实例属性在实例化之后却不能作为原型属性。

[javascript] view plaincopy

  1. <html>  

  2.     <head>  

  3.         <meta http-equiv="Content-Type" content="text/html; charset=utf-8">  

  4.         <title>测试</title>  

  5.     </head>  

  6.     <body>  

  7.         <mce:script type="text/javascript"><!--  

  8.             function Man(name, age) {  

  9.                 //定义实例属性  

  10.                 this.name = name;  

  11.                 this.age = age;  

  12.             }  

  13.               

  14.             //定义静态属性。默认性别是男,不排除变性,^v^  

  15.             Man.sex = '男';  

  16.             //定义原型属性  

  17.             Man.prototype.phone = '123456';  

  18.             //除了name.sex和Man.prototype.phone其余全部是Undefined  

  19.             alert(Man.sex + "-----" + Man.prototype.phone + "--" + Man.name + "--" + Man.age + "--" + Man.phone);  

  20.             var man = new Man("Tom", 24);  

  21.             alert(Man.sex + "--" + man.name + "--" + man.age + "--" + man.phone);  

  22.             /** 

  23.              * 通过例子说明:原型属性在实例化之后作为类的实例属性。 

  24.              * 但是实例属性在实例化之后却不能作为原型属性。 

  25.              */  

  26.           

  27. // --></mce:script>  

  28.     </body>  

  29. </html>  


2

javascript变量的作用域

不太会写冠冕堂皇的开场白,直接进入主题。

我们看一道题,出处为javaeye的某贴——这世界就是这样,有些人喜欢制造问题,有人喜欢解决问题。制造问题的人为解决问题的人带来就业机会……

var  a=100;
var  b= true ;
function  test(){
alert(a);
alert(b);
b= false ;
alert(b);
var  a=200;
alert(a/2);
alert(++Math.PI);
alert(Math.PI++);
}
test();

运行代码

为什么第一个alert为undefined,而第二个为true。这问题也可以延伸为——alert(b)时怎么就会找外部的b,而alert(a)时就不会往外面找?!

我们都明白局部变量的优先级大于全局变量,或者说内围作用域的变量的优先级比外围的高。当JS引擎在当前作用域找不到此变量时,它就往外围的作用域找。不过,在这之前,有一个严肃的问题是,究竟当前作用域存不存在这个变量。像javascript这样的解释型语言,基本分为两个阶段,编译期(下面为符合大多数语言的称呼习惯,改叫预编译)与运行期。在预编译阶段,它是用函数来划分作用域,然后逐层为其以 var 声明的变量(下略称为var变量)与函数定义开辟内存空间,再然后对var变量进行特殊处理,统统赋初始值为undefined,如下图:

由上图,我们便可以推知,当前网页拥有两个a,一个b,一个test函数。如果在运行期用到除此以外的东东,如c函数或d变量啦,就会报未定义错误(用eval等非正常手段生成变量与函数的情况除外),此外,它们最多出现未赋值警告。

javascript的运行期是在为var变量与函数定义分配空间后立即执行,并且是逐行往下执行的。

  • 第1行它为外围作用域的a赋值为100

  • 第2行它为外围作用域的b赋值为true

  • 第3行进行test的作用域,我们简称为内围作用域。

  • 第4行就立即调用内围作用域的a,这时它还没有来得及赋值呢!不过它已经声明过了,因此默认为其赋值为undefined(在预编译阶段,见图),于是alert为undefined

  • 第5行就调用b时,JS引擎就拿起我画的图看了(笑),发现test的作用域内没有b,眼睛往外望,发现b了,而b在第二行就赋值为true,于是alert为true。

  • 第6行为一个赋值操作,把外围的b变量改赋为false。于是到第7行时,alert为false。以下说法不说了。

作为对比,我们改写一下例子:

var  a=100;
var  b= true ;
function  test(){
alert(a);
alert(b);
var  b= false ;
alert(b);
var  a=200;
alert(a/2);
alert(++Math.PI);
alert(Math.PI++);
}
test();

这时在test函数的作用域内,b也被声明了。

掌握预编译为var变量与函数定义分配空间这一事实后,许多问题就迎刃而解。我们看犀牛书上的一个例子。

var  scope =  "global" ;
function  f() {
alert(scope);
var  scope =  "local" ;
alert(scope);
}
f();

答案呼之欲出!

我们来看更复杂的例子。

Object.prototype.test =  'wrong' ;
var  test =  'right' ;
( function  f() {
alert(test);
})();

这个问题的难点在于,运行期时,又生成一同名变量,它是附着于Object.prototype,究竟哪一个距离F()的作用域近一些呢?!测试结果是var test。于是我们有了下图:

于是我们明白了,原来定义在函数外面的var变量并不位于window作用域的下一层。

我们继续加深难度。

( function  f() {
alert(test);
})();
Object.prototype.test =  'ccc' ;
Object.test =  "bbb"
window.test =  "aaa" ;

报未定义错误,因为预编译期时没有符合要求的var变量,而在运行期时,和它同名的变量在调用时(第2行)也尚未建立起来!

如果这样呢?!

Object.test =  "bbb" ;
Object.prototype.test =  'ccc' ;
window.test =  "aaa" ;
( function  f() {
alert(test);
})();

估计有很多人猜是bbb,其实是ccc!有人就不解了,不是对象的属性的优先级比其原型属性的优先级高吗???

无错,的确如此,不过对象的属性是这样定义的:

var  o = {test: "eee" }

Object.test = “XXX”这样定义,为类属性,或称静态属性。类属性优先级都是比实例属性低的

通过这图也教育我们,一定要用局部变量啊,要不,一层层往上爬,效率是多么低啊。另外,这图也告诉我们,window是一个多么高级的存在啊(微软最爱听),Object都比它低一等,更别提什么继承问题啦!(在FF与IE中)

//颠覆常识的存在
alert(window  instanceof  Object);

运行代码

搞定这个我们看最难的一题。类属性,实例属性,原型属性,极晚绑定的属性都考到了!不过实例属性与类属性已超出本文的讨论范围,恕不讨论了。这些也不太难,很容易google到的,自己google吧。

function  foo(){
foo.abc =  function (){alert( 'def' )}
this .abc =  function (){alert( 'xyz' )}
abc =  function (){alert( '@@@@@' )};
var  abc =  function (){alert( '$$$$$$' )}
}
foo.prototype.abc =  function (){alert( '456' );}
foo.abc =  function (){alert( '123' );}
var  f =  new  foo();
f.abc();
foo.abc();
abc();

蓝色理想的人说这题出得不好,有错。我说,有错才好,这样才能考出水平!十秒内做出正确答案,说明学会了。

答案:

运行代码

最后归纳一下,JS引擎有两个设置变量的机会。第一次在预编译时期,所有var变量会分配到各自的作用域中,值一律为undefined。第二次在运行期,由于是逐行执行,因此是可变的。我们可以通过eval与Function动态生成新的变量,它们的作用域都是可制定的,其他赋值语句,只是把变量固定于顶层作用域(window)中,或是仅仅是重新赋值。我们也可以用delete来删除对象的属性,迫使其往外走同名变量。with闭包会在其引用的对象的属性被删除后,在闭包的外围寻找与此属性同名的变量。


转载于:https://my.oschina.net/u/1866821/blog/307134

<think>好的,用户想了解变量作用域的概念及用法,特别是定义和使用范围。首先,我需要整理变量作用域的基本定义,然后分类型说明,比如全局、局部、块级作用域等。根据提供的引用,用户可能对Python、Shell和JS作用域差异感兴趣,需要分别举例说明。 首先,明确变量作用域变量在代码中的可访问范围。接下来,按照引用中的信息,引用[1]提到Python的局部和全局变量,引用[2]讲Shell的local命令,引用[3]涉及JS的全局和函数作用域,引用[4]强调全局变量初始化的时序问题。需要结合这些内容,解释不同语言中的作用域规则。 然后,考虑使用范围,要说明在不同作用域变量的生命周期和访问权限。比如全局变量在整个程序运行期间存在,而局部变量仅在函数执行时存在。同时,块级作用域(如JS的let/const)在ES6中的表现可能需要提及,尽管引用中没有直接提到,但这是常见知识点,可以补充。 注意事项部分,根据引用[4],需要强调全局变量的初始化顺序和谨慎使用,避免耦合问题。同时,不同语言的作用域差异,如Shell中使用local定义局部变量,而Python默认函数内部变量为局部,除非声明global。 最后,生成相关问题时要覆盖不同语言的作用域特点、最佳实践和应用场景,帮助用户深入理解。确保回答结构清晰,分点明确,结合示例代码(如引用中的Shell脚本)来增强理解。检查是否符合格式要求,如数学表达式用$...$,但这里可能用不上。注意引用标识的位置,自然添加在相关解释末尾。</think>编程中的变量作用域定义了变量的可见性和生命周期,是影响代码逻辑、内存管理的重要概念。以下是不同编程语言中作用域的核心规则与典型用法: --- ### 一、作用域类型与定义 1. **全局作用域** - 变量在代码最外层定义(如脚本顶层、函数外部),**整个程序运行期间有效** - 示例(Python): ```python global_var = 10 # 全局变量 def func(): print(global_var) # 可访问 ``` [^1] 2. **局部作用域** - 在函数或代码块内部定义,**仅在其所属范围内有效** - Shell中需用`local`显式声明(引用[2]示例): ```bash function func() { local a=99 # 局部变量 } ``` 3. **块级作用域**(如JavaScript ES6+) - 通过`let`/`const`定义的变量仅在代码块(如`{}`)内有效 - 示例: ```javascript if (true) { let blockVar = 5; // 仅在此块中有效 } ``` [^3] --- ### 二、作用域优先级与覆盖规则 | 场景 | 结果 | 示例(Python) | |---------------------|-----------------------|-----------------------------------| | 全局与局部同名 | 局部变量优先 | 函数内使用局部变量,外部仍为全局变量 | | 嵌套函数作用域 | 就近访问(闭包特性) | 内部函数可访问外部函数的变量 | | 显式声明(如`global`)| 修改全局变量 | 需在函数内用`global x`声明 | --- ### 三、关键注意事项 1. **全局变量谨慎使用** - 易引发耦合问题,初始化时序需严格分析(如系统启动阶段依赖)[^4] - 示例风险场景: ```python # 若init()未先执行,global_var可能未定义 def use_global(): print(global_var) # 可能报错 ``` 2. **语言差异性** - **JavaScript**:变量提升(`var`)、暂时性死区(`let/const`) - **Shell**:默认全局,函数内需用`local`限制作用域[^2] - **C/C++**:静态变量(`static`)延长局部变量生命周期 --- ### 四、最佳实践 1. **最小化作用域原则** - 优先使用局部变量,避免全局污染 2. **命名规范** - 全局变量用全大写+下划线(如`GLOBAL_CONFIG`) 3. **闭包管理** - 及时释放不再使用的引用,防止内存泄漏 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值