`
zhouyrt
  • 浏览: 1162677 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Javascript中同名标识符优先级

 
阅读更多


一,局部变量先使用后声明,不影响外部同名变量

var x = 1; // --> 外部变量x
function fn(){
	alert(x);  // --> undefined 局部变量x先使用
	var x = 2; // 后声明且赋值
}
fn();
alert(x); // --> 1

第一点,函数fn内第一句输出x,x是在第二句才定义的。这在js中是允许的,这里的允许是指不会出现语法错误程序可以运行。
但在其它语言如C,Java中却是不允许的。变量必须先声明后使用,如:

public class Test {
	public static void main(String[] args) {
		System.out.println(x); // 先使用
		int x = 10; // 后声明
	}
}

 

Java中编译器会提示错误,程序无法运行。

第二点,函数fn内的局部变量x不会影响到外部的变量x。即fn内alert输出不是1,而是undefined。

 

二,形参优先级高于函数名

function fn(fn){
	alert(fn);
}
fn('hello'); // --> "hello"

 

可以看到函数名和形参同名都是fn,输出的是字符串"hello",却不是函数fn的函数体(fn.toString())。

 

 

三,形参优先级高于arguments

function fn(arguments){
	alert(arguments);
}
fn('hello'); // --> "hello"

 

arguments对象可以直接在函数内使用,是语言本身提供的一个 特殊标识符
这里刚好将形参声明成与其同名。输出可以看到是"hello"而非"[object Object]",即形参arguments覆盖了语言本身提供的真正的arguments。

 

 

四,形参优先级高于只声明却未赋值的局部变量

function fn(a){
	var a;
	alert(a);
}
fn('hello'); // --> "hello"

 

函数fn形参为a,函数内第一句仅声明局部变量a,却并未赋值。从输出结果是"hello"而非undefined可以看出形参a优先级高于仅声明却未赋值的局部变量a。

 

 

五,声明且赋值的局部变量优先级高于形参

function fn(a){
	var a = 1;
	alert(a);
}
fn('hello'); // --> "1"

 

函数fn形参为a,函数内第一句仅声明局部变量a,赋值为1。从输出结果是"1"而非"hello"可以看出声明且赋值的局部变量a优先级高于形参a。

 

 

六,形参赋值给同名局部变量时

function fn(a){
	var a = a;
	alert(a);
}
fn('hello');

 

暂不运行,猜测下结果。如果按照第五点:声明且赋值的局部变量优先级高于形参。那么a将是undefined。但实际上a是"hello",即右a是形参a,左a才是局部变量a。

 

 

 

这里的两个a互不干扰,谁也没覆盖谁。这与刚刚说的赋值的局部变量优先级高于形参又矛盾了。但引擎这样做的确是我们想要的,因为并不希望var a = a后a是undefined。

 

  • 大小: 13.8 KB
分享到:
评论
19 楼 zhouyrt 2011-02-07  
谢谢wmingjian发表的很多见解。这里只记录下现象(客观存在),加深记忆。

你所示例的fn1,fn2分别于我总结的第四,第五点一致。

这里未讨论ECMAScript的规定及各浏览器是否完全遵循其来实现。

18 楼 wmingjian 2011-02-06  
给一个测试用例,分析一下两个的区别:
function fn1(a){
  var a;
  alert(a);
  alert(a === arguments[0]);
}
fn1('hello'); // --> "hello"

function fn2(a){
  var a = 'hello';
  alert(a);
  alert(a === arguments[0]);
}
fn2(); // --> "hello"

认同clue的观点,认同ECMAScript。同时,具体实现可能和规范不一致,分两种情况,一些没有实现规范所要求,另一些自行做了规范没有约定到的范畴的事情。都了解之后,才能辨清哪些是浮云,哪些是事实。
17 楼 endall 2010-11-25  
<p>   1. var x = 1; // --&gt; 外部变量x  <br>   2. function fn(){  <br>   3.     alert(x);  // --&gt; undefined 局部变量x先使用  <br>   4.     var x = 2; // 后声明且赋值  <br>   5. }  <br>   6. fn();  <br>   7. alert(x); // --&gt; 1  </p>
<p>出现undefined的原因:</p>
<p>1、javascript是词法作用域(也叫静态作用域),即词法分析时的作用域。</p>
<p>2、第四行x的作用域是fn函数内,第3行alert(x)会优先使用第4行的x,而非第1行的x,当第3行使用x的时候,发现<span style="color: #ff00ff;"><span style="background-color: #ffffff;">先使用后声明</span></span></p>
16 楼 yuankai 2010-11-25  
kjj 写道
变量一定要同名吗,同名有什么必要性,有啥好处!!!

没人告诉你有什么必要,也没有好处。
只是举例告诉你js解析方式。
15 楼 sswh 2010-11-25  
clue 写道
对你总结及推测并不赞同,你把声明和赋值弄一起了,这样测试出的结果反而偏离了核心。


赞同!!尤其是形参和局部变量的优先级觉得楼主总结的很混乱。
14 楼 沙舟狼客 2010-11-24  
很详细,值得学习
13 楼 whiletrue 2010-11-21  
     test();//1    
     var test1 = function test ( ){    
            alert( "test1" );    
     }    //test 第一次声明
      test1();//2  
     function test(){    
            alert( "test2" );    
     }    //test被覆盖
      test();//3  2


test();//1    
    function test(){    
            alert( "test2" );    
     } //test第一次声明
     var test1 = function test ( ){    
            alert( "test1" );    
     }    //test被覆盖
test();//
      test1();//
        
      


这样跑跑就全清楚了,谢谢clue,在IE下确实是这个样子
12 楼 whiletrue 2010-11-21  
请教一下clue,你在下面这个帖子
http://www.iteye.com/topic/802482#1741032
里面提到
var a = function b(){  
   alert("test");  
};  

// 按标准,此处应出错,因为b未定义。 
alert(a === b); // IE下为false... IE sucks 
它可以看作类似于以下代码:
function b(){  
   alert("test");  
}  
var a = function(){  
   alert("test");  
};  
alert(a === b); 

如果是这样的话,为什么函数b的定义没有被覆盖呢?
如果是等同上面的代码,那么
test();//1  
     var test1 = function test ( ){  
            alert( "test1" );  
     }  
      test1();//2  按照上面的说法,这里test 应该被覆盖了
     function test(){  
            alert( "test2" );  
     }  
      test();//3  2执行完后,这里alert应该是test1了
11 楼 cloudgamer 2010-11-19  
跟排版相比内容显得有点幼稚
10 楼 zhouyrt 2010-11-18  
谢谢大家的讨论。有空我好好学习下。
9 楼 cuixiping 2010-11-18  
clue 真乃神人也
说实话我完全没读过ECMA Script,看来有必要读读了
8 楼 clue 2010-11-18  
pfans 写道
赞楼主。楼主把各种情况下同名标识符优先级一一列出,说明楼主很有心,关注细节。解疑了困惑我很久的问题。但结尾应该把所有讨论情况总结下。应该是这样的:

局部变量(vars) > 形式参数(formal parameter) > arguments
                                            > 函数名

发完牢骚,再来讨论你的结论。
老实说,其实不用扯什么解释期,执行期,你的结论本身就没分清声明和执行。
在执行前,参数声明最前,函数声明如果有冲突就会覆盖
arguments及局部变量如果有冲突就跳过

要说优先,只有初始值可以讨论讨论(声明完后就是变量,可以随赋值修改)
函数声明最优先(会覆盖参数,不会被其它人覆盖),其次参数声明(后面没人会覆盖它的值),然后arguments(局部变量只声明,不覆盖),最后是局部变量(初始值undefined)。
7 楼 clue 2010-11-18  
pfans 写道

此外clue对ECMAScript的理解太认同,比如忽视了最基本的JS中“解释期”,“运行期”。将两者混为谈。
显然他没有理解执行上下文栈(execution context stack),执行上下文(execution context),变量对象(variable object),激活对象(activation object)等。
而是引用一大段ECMAScript 10.5节 去表达自己对ECMAScript的学习和理解。

不知道你是怎么得出这个结论的,倒像是你没有仔细看
在我的描述中
scope chain = execution context stack
执行环境 = execution context

然后,一个执行环境都必有一个关联的变量对象,一般就把它整合进执行环境一起说了。
激活对象是ECMAScript中的抽象概念,也只是描述当前唯一的处于运行状态的执行环境而已,第5版中甚至把这玩艺给拿掉了。

最后,不知道你有什么高见,难不成你可以整理一份比ECMAScript文档所描述得更精简清晰的文档,来解释所有这些JS解释执行库的行为?

在标准化的潮流下,不认同ECMAScript,你想去认同什么?

P.S.
我的回帖没有任何针对楼主的意思,事实上楼主所发的前几个帖都成功引发我的兴趣,从而导致我去翻ECMAScript文档,试图去弄明白。
我的回帖也是弄懂文档,完全理解以上所有现象后整理出来的,可以看作一个学习笔记
没有楼主之前所发的一系列帖子,也不会有现在对ECMAScript文档的理解。
6 楼 pfans 2010-11-18  
赞楼主。楼主把各种情况下同名标识符优先级一一列出,说明楼主很有心,关注细节。解疑了困惑我很久的问题。但结尾应该把所有讨论情况总结下。应该是这样的:

局部变量(vars) > 形式参数(formal parameter) > arguments
                                            > 函数名

clue 的回复我觉得是从更深层次(引擎内核)去证明楼主所说的优先级次序。并不矛盾。即楼主摆出了事实,clue 试图去说明事实的深层原因。

此外clue对ECMAScript的理解太认同,比如忽视了最基本的JS中“解释期”,“运行期”。将两者混为谈。
显然他没有理解执行上下文栈(execution context stack),执行上下文(execution context),变量对象(variable object),激活对象(activation object)等。
而是引用一大段ECMAScript 10.5节 去表达自己对ECMAScript的学习和理解。

另外几个回复只能说没认真看帖,看见某“高人”引用ECMAScript就觉得很牛。

总之,帖子是好贴。要细读,测试,体会。
5 楼 kjj 2010-11-17  
变量一定要同名吗,同名有什么必要性,有啥好处!!!
4 楼 暴走的酱油瓶 2010-11-17  
支持clue的观点,在同一scope下的变量多次声明是合法的且也不会引起错误。(It is legal and harmless to declare a variable more than once with the var statement.)

 fn();  

 function fn(){  
     var a = 1;  
     var a;
     alert(a);
 }


PS:自从“写了10年Javascript未必全了解的连续赋值运算”那贴里看了clue回复,发现以前好多自己测试出来的猜测结论,都在读了ECMAScript后清楚了。

clue 写道

函数声明(FunctionDeclaration)的分辨请见:http://www.iteye.com/topic/802482#1741032
(不知道是谁投的隐藏帖,无语了)

这贴的亮点其实就是clue的回复,投隐藏确实有点可惜。


3 楼 smzd 2010-11-17  
2楼正解。楼主加油啊
2 楼 soni 2010-11-16  
你们讨论的太复杂了......
1 楼 clue 2010-11-16  
对你总结及推测并不赞同,你把声明和赋值弄一起了,这样测试出的结果反而偏离了核心。

ECMAScript 5th中,函数执行的处理顺序(简要说明,详细的见帖后的原文)
首先进行执行环境的创建{
  先进行形参声明及赋值
  再进行函数声明及赋值
  如果arguments在当前环境未声明,则声明并初始化。否则不作处理
  最后进行函数体内的变量声明
}
然后才是执行函数体语句
例如代码中的var a = 1,在函数执行前就处理了a的声明,执行时只是赋值a=1而已

再分析你写的示例:
第2节:
function fn(fn){
	alert(fn);
}
fn('hello'); // --> "hello"

首先,这段代码运行前会先处理函数声明,在全局创建了fn这个变量。
然后执行函数体:fn('hello'),这时进入新的函数,先创建执行环境:
声明形参fn,值为'hello'
然后执行函数体:alert(fn)
对于标识符(identifier)的取值,先从当前环境查找,找到了'hello'。
(如果没找到,往上层环境查找,那就是函数定义时的环境了,即全局,它也有fn。参考scope chain)

第4节:
function fn(a){
	var a;
	alert(a);
}
fn('hello'); // --> "hello"

fn调用时创建执行环境:
声明形参a,值为'hello'
处理变量声明,a已经声明,不作处理
(处理函数体内变量声明时,如果已经声明过,不作处理。未声明时才声明并设置为undefined)

第5节:
function fn(a){
	var a = 1;
	alert(a);
}
fn('hello'); // --> "1"

执行环境建立:
(同第4节)
声明形参a,值为'hello'
处理变量声明,a已经声明,不作处理
执行函数体:
a = 1  // 修改变量a的值为1
...
(没什么优先级,a = 1这一句的a,还是参数声明时创建的标识符。同4节,函数体内变量重复声明不作任何处理)

第6节:
function fn(a){
	var a = a;
	alert(a);
}
fn('hello');

执行环境建立:
(同第4,5节)
声明形参a,值为'hello'
处理变量声明,a已经声明,不作处理
执行函数体:
a = a  // 变量a的值不变,还是为hello
...


附1
ECMAScript 5th中的声明绑定过程(Declaration Binding Instantiation)及部分翻译(个人理解,译得不准请见谅):
引用

10.5  Declaration Binding Instantiation
Every execution context has an associated VariableEnvironment. Variables and functions declared in ECMAScript code evaluated in an execution context are added as bindings in that VariableEnvironment’s Environment Record. For function code, parameters are also added as bindings to that Environment Record.
Which Environment Record is used to bind a declaration and its kind depends upon the type of ECMAScript code executed by the execution context, but the remainder of the behaviour is generic. On entering an execution context, bindings are created in the VariableEnvironment as follows using the caller provided code and, if it is function code, argument List args: // 设args为实参数组

1.  Let env be the environment record component of the running execution context’s VariableEnvironment.
2.  If code is eval code, then let configurableBindings be true else let configurableBindings be false.
3.  If code is strict mode code, then let strict be true else let strict be false.
4.  If code is function code, then 
a.  Let func be the function whose [[Call]] internal method initiated execution of code. Let names be the value of func’s [[FormalParameters]] internal property.   // 设func为函数,names为形参数组
b.  Let argCount be the number of elements in args. // 设argCount为实参个数
c.  Let n be the number 0. // 设n为0
d.  For each String argName in names, in list order do // 顺序遍历形参数组,对每个形参名argName作处理
i.  Let n be the current value of n plus 1. // n=n+1 (即n为当前形参序号,从1开始)
ii.  If n is greater than argCount, let v be undefined otherwise let v be the value of the n’th element of args. // 如果n大于形参数目,则设v为undefined。否则v为对应的实参值
iii.  Let argAlreadyDeclared be the result of calling env’s HasBinding concrete method passing argName as the argument. // 设argAlreadyDeclared为[当前环境.标识符是否已声明(argName)]的返回值
iv.  If argAlreadyDeclared is false, call env’s CreateMutableBinding concrete method passing argName as the argument. // 如果为false,即未声明,则创建之
v.  Call env’s SetMutableBinding concrete method passing argName, v, and strict as the arguments. // 设置环境中的argName变量值为v
5.  For each FunctionDeclaration f in code, in source text order do // 对于函数声明,按代码先后作处理(对于函数声明详细信息,见附2)
a.  Let fn be the Identifier in FunctionDeclaration f.
b.  Let fo be the result of instantiating FunctionDeclaration f as described in Clause 13.
c.  Let funcAlreadyDeclared be the result of calling env’s HasBinding concrete method passing fn as the argument.
d.  If funcAlreadyDeclared is false, call env’s CreateMutableBinding concrete method passing fn and configurableBindings as the arguments. // 如果标识符未声明,则声明之
e.  Call env’s SetMutableBinding concrete method passing fn, fo, and strict as the arguments. // 设置值
6.  Let argumentsAlreadyDeclared be the result of calling env’s HasBinding concrete method passing "arguments" as the argument  // 设argumentsAlreadyDeclared为[当前环境.标识符是否已声明("arguments")]的返回值
7.  If code is function code and argumentsAlreadyDeclared is false, then // 如果未声明,则处理
a.  Let argsObj be the result of calling the abstract operation CreateArgumentsObject (10.6) passing  func, names, args, env and strict as arguments.
b.  If strict is true, then 
i.  Call env’s CreateImmutableBinding concrete method passing the String "arguments" as the argument.
ii.  Call env’s InitializeImmutableBinding concrete method passing "arguments" and argsObj as arguments.
c.  Else,
i.  Call env’s CreateMutableBinding concrete method passing the String "arguments" as the argument.
ii.  Call env’s SetMutableBinding concrete method passing "arguments", argsObj, and false as arguments.
8.  For each VariableDeclaration and VariableDeclarationNoIn d in code, in source text order do // 按代码顺序处理变量声明
a.  Let dn be the Identifier in d.
b.  Let varAlreadyDeclared be the result of calling env’s HasBinding concrete method passing dn as the argument.
c.  If varAlreadyDeclared is false, then // 如果未声明
i.  Call env’s CreateMutableBinding concrete method passing dn and configurableBindings as the arguments. // 声明标识
ii.  Call env’s SetMutableBinding concrete method passing dn, undefined, and strict as the arguments. // 设值为undefined


附2
函数声明(FunctionDeclaration)的分辨请见:http://www.iteye.com/topic/802482#1741032
(不知道是谁投的隐藏帖,无语了)

相关推荐

    浅析JavaScript中的同名标识符优先级

    同名标识符优先级指的是在不同作用域中存在相同名称的标识符时,JavaScript引擎如何决定使用哪一个标识符。这涉及到变量作用域、提升(hoisting)、参数传递、函数声明、函数表达式等方面的知识点。 首先,关于局部...

    学习笔记

    6. **Javascript中同名标识符优先级** JavaScript允许在不同作用域内定义同名标识符,但其解析规则遵循作用域链。理解这一点对于避免变量冲突和正确使用闭包至关重要。 7. **java实现高性能的数据同步** 在多线程...

    浅谈js中同名函数和同名变量的执行问题

    如果在闭包中定义了同名的函数和变量,那么这些同名的标识符会根据作用域链上的位置进行访问,而不是简单的覆盖。在闭包中,函数的创建比变量的赋值拥有更高的优先级。也就是说,即使后续定义了同名的变量,内部函数...

    深入解读JavaScript中的Hoisting机制

    JavaScript中的变量和函数声明都有各自的优先级。按优先级顺序排列,它们分别是: - 语言内置的对象,如`this`和`arguments`; - 形式参数,即函数定义时的参数; - 函数声明; - 变量声明。 如果变量声明和函数...

    青软实训web考试题.pdf

    8. JavaScript标识符命名规范:标识符的第一个字符不能是数字,但可以是字母、下划线或美元符号。JavaScript是区分大小写的,且标识符不能与保留字相同。 9. CSS选择器优先级:ID选择器 &gt; 类选择器 &gt; 标签选择器 &gt; ...

    青软实训web考试题.docx

    在JavaScript中,使用模板字符串的方式是 `var name = `漫步时尚广场`;`,即选项 A 是正确的。 ### 六、JavaScript标识符的命名规则 1. **首字符限制**:标识符的第一个字符必须是字母、下划线或美元符号。 2. **...

    浅谈js中的变量名和函数名重名

    在JavaScript编程语言中,变量名和函数名重名是一个常见的问题,这主要涉及到JavaScript的预解析(hoisting)机制和作用域规则。本文将深入探讨这个问题,并解释为什么在某些情况下,代码可能会出现“is not a ...

    2021-2022计算机二级等级考试试题及答案No.919.docx

    2. **标识符的合法性**:合法的标识符在编程语言中用于变量、函数等命名,必须以字母、下划线开头,后续可跟字母、下划线或数字。在给定选项中,Hello_World和username123是合法的,而class和123username不是。 3. ...

    最新JAVA等级考试题库.docx

    - **方法重载**(选项C):Java支持在一个类中定义多个同名方法,只要这些方法的参数列表不同即可,这被称为方法重载。 ### 9. Applet初始化方法 - **Applet的初始化方法**(选项D):在Java Applet程序中,通常...

    web标准培训教案-CSS+DIV

    DOM(Document Object Model)用于操作网页内容,以及ECMAScript(JavaScript的标准化版本)用于实现交互行为。这一系列规范旨在使网页更具可访问性、可维护性和互操作性。 2. **为什么要使用Web标准**: - **协同...

    2021-2022计算机二级等级考试试题及答案No.18479.docx

    - **概念**:在同一个类中可以存在一个以上的同名方法,只要这些方法的参数列表不同(参数个数、参数类型不同)或参数顺序不同即可。 - **正确答案解析**:选项 ACD 正确。选项 B 描述的情况实际上是方法重载的一个...

    2021-2022计算机二级等级考试试题及答案No.14430.docx

    - **知识点概述**:方法重载是在同一个类中定义多个同名的方法,但它们的参数列表不同。 - **详细解释**:方法重载的关键在于方法签名(即方法名和参数列表)的不同,即使返回类型相同,只要参数个数、类型或顺序...

Global site tag (gtag.js) - Google Analytics