javascript变量声明 及作用域

javascript变量声明提升(hoisting)

http://openwares.net/js/javascript_declaration_hoisting.html 可能要FQ一下

javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面。

先看一段代码


1

2

3

4

5

var v = "hello";

(function(){

  console.log(v);

  var v = "world";

})();

这段代码运行的结果是什么呢?
答案是:undefined
这段代码说明了两个问题,
第一,function作用域里的变量v遮盖了上层作用域变量v。代码做少些变动


1

2

3

4

5

var v = "hello";

if(true){

  console.log(v);

  var v = "world";

}

输出结果为”hello”,说明javascript是没有块级作用域的函数是JavaScript中唯一拥有自身作用域的结构。

第二,在function作用域内,变量v的声明被提升了。所以最初的代码相当于:


1

2

3

4

5

6

var v = "hello";

(function(){

  var v; //declaration hoisting

  console.log(v);

  v = "world";

})();

声明、定义与初始化

声明宣称一个名字的存在,定义则为这个名字分配存储空间,而初始化则是为名字分配的存储空间赋初值。
用C++来表述这三个概念


1

2

3

extern int i;//这是声明,表明名字i在某处已经存在了

int i;//这是声明并定义名字i,为i分配存储空间

i = 0;//这是初始化名字i,为其赋初值为0

javascript中则是这样


1

2

var v;//声明变量v

v = "hello";//(定义并)初始化变量v

因为javascript为动态语言,其变量并没有固定的类型,其存储空间大小会随初始化与赋值而变化,所以其变量的“定义”就不像传统的静态语言一样了,其定义显得无关紧要。

声明提升

当前作用域内的声明都会提升到作用域的最前面,包括变量和函数的声明


1

2

3

4

5

6

(function(){

  var a = "1";

  var f = function(){};

  var b = "2";

  var c = "3";

})();

变量a,f,b,c的声明会被提升到函数作用域的最前面,类似如下:


1

2

3

4

5

6

7

(function(){

  var a,f,b,c;

  a = "1";

  f = function(){};

  b = "2";

  c = "3";

})();

请注意函数表达式并没有被提升,这也是函数表达式与函数声明的区别。进一步看二者的区别:


1

2

3

4

5

6

7

8

9

(function(){

  //var f1,function f2(){}; //hoisting,被隐式提升的声明

  f1(); //ReferenceError: f1 is not defined

  f2();

  var f1 = function(){};

  function f2(){}

})();

上面代码中函数声明f2被提升,所以在前面调用f2是没问题的。虽然变量f1也被提升,但f1提升后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。所以只有声明是被提升的。

名字解析顺序

javascript中一个名字(name)以四种方式进入作用域(scope),其优先级顺序如下:
1、语言内置:所有的作用域中都有 this 和 arguments 关键字
2、形式参数:函数的参数在函数作用域中都是有效的
3、函数声明:形如function foo() {}
4、变量声明:形如var bar;

名字声明的优先级如上所示,也就是说如果一个变量的名字与函数的名字相同,那么函数的名字会覆盖变量的名字,无论其在代码中的顺序如何。但名字的初始化却是按其在代码中书写的顺序进行的,不受以上优先级的影响。看代码:


1

2

3

4

5

6

7

8

9

(function(){

    var foo;

    console.log(typeof foo); //function

    

    function foo(){}

    foo = "foo";

    console.log(typeof foo); //string

})();

如果形式参数中有多个同名变量,那么最后一个同名参数会覆盖其他同名参数,即使最后一个同名参数并没有定义。

以上的名字解析优先级存在例外,比如可以覆盖语言内置的名字arguments。

命名函数表达式

可以像函数声明一样为函数表达式指定一个名字,但这并不会使函数表达式成为函数声明。命名函数表达式的名字不会进入名字空间,也不会被提升。


1

2

3

4

5

f();//TypeError: f is not a function

foo();//ReferenceError: foo is not defined

var f = function foo(){console.log(typeof foo);};

f();//function

foo();//ReferenceError: foo is not defined

命名函数表达式的名字只在该函数的作用域内部有效。
===
在认识一切事物之后,人才能认识自己,因为事物仅仅是人的界限。 —— 尼采

Js作用域与作用域链详解

http://blog.csdn.net/yueguanghaidao/article/details/9568071

一直对Js的作用域有点迷糊,今天偶然读到Javascript权威指南,立马被吸引住了,写的真不错。我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫。

一:函数作用域

先看一小段代码:

[javascript] view plaincopy

  1. var scope="global";
  2. function t(){
  3. console.log(scope);
  4. var scope="local"
  5. console.log(scope);
  6. }
  7. t();

(PS: console.log()是firebug提供的调试工具,很好用,有兴趣的童鞋可以用下,比浏览器+alert好用多了)

第一句输出的是: "undefined",而不是 "global"

第二讲输出的是:"local"

你可能会认为第一句会输出:"global",因为代码还没执行var scope="local",所以肯定会输出“global"。

我说这想法完全没错,只不过用错了对象。我们首先要区分Javascript的函数作用域与我们熟知的C/C++等的块级作用域。

在C/C++中,花括号内中的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的。而Javascript压根没有块级作用域,而是函数作用域.

所谓函数作用域就是说:-》变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。

所以根据函数作用域的意思,可以将上述代码重写如下:

[javascript] view plaincopy

  1. var scope="global";
  2. function t(){
  3. var scope;
  4. console.log(scope);
  5. scope="local"
  6. console.log(scope);
  7. }
  8. t();

我们可以看到,由于函数作用域的特性,局部变量在整个函数体始终是由定义的,我们可以将变量声明”提前“到函数体顶部,同时变量初始化还在原来位置。

为什么说Js没有块级作用域呢,有以下代码为证:

[javascript] view plaincopy

  1. var name="global";
  2. if(true){
  3. var name="local";
  4. console.log(name)
  5. }
  6. console.log(name);

都输出是“local",如果有块级作用域,明显if语句将创建局部变量name,并不会修改全局name,可是没有这样,所以Js没有块级作用域。

现在很好理解为什么会得出那样的结果了。scope声明覆盖了全局的scope,但是还没有赋值,所以输出:”undefined“。

所以下面的代码也就很好理解了。

[javascript] view plaincopy

  1. function t(flag){
  2. if(flag){
  3. var s="ifscope";
  4. for(var i=0;i<2;i++)
  5. ;
  6. }
  7. console.log(i);
  8. console.log(s);
  9. }
  10. t(true);

输出:2  ”ifscope"

二:变量作用域

还是首先看一段代码:

[javascript] view plaincopy

  1. function t(flag){
  2. if(flag){
  3. s="ifscope";
  4. for(var i=0;i<2;i++)
  5. ;
  6. }
  7. console.log(i);
  8. }
  9. t(true);
  10. console.log(s);

就是上面的翻版,知识将声明s中的var去掉。

程序会报错还是输出“ifscope"呢?

让我揭开谜底吧,会输出:”ifscope"

这主要是Js中没有用var声明的变量都是全局变量,而且是顶层对象的属性。

所以你用console.log(window.s)也是会输出“ifconfig"

当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除

var name=1    ->不可删除

sex=”girl“         ->可删除

this.age=22    ->可删除

三:作用域链

先来看一段代码:

[javascript] view plaincopy

  1. name="lwy";
  2. function t(){
  3. var name="tlwy";
  4. function s(){
  5. var name="slwy";
  6. console.log(name);
  7. }
  8. function ss(){
  9. console.log(name);
  10. }
  11. s();
  12. ss();
  13. }
  14. t();

当执行s时,将创建函数s的执行环境(调用对象),并将该对象置于链表开头,然后将函数t的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显

name是"slwy"。

但执行ss()时,作用域链是: ss()->t()->window,所以name是”tlwy"

下面看一个很容易犯错的例子:

[html] view plaincopy

  1. <html>
  2. <head>
  3. <script type="text/javascript">
  4. function buttonInit(){
  5. for(var i=1;i<4;i++){
  6. var b=document.getElementById("button"+i);
  7. b.addEventListener("click",function(){ alert("Button"+i);},false);
  8. }
  9. }
  10. window.onload=buttonInit;
  11. </script>
  12. </head>
  13. <body>
  14. <button id="button1">Button1</button>
  15. <button id="button2">Button2</button>
  16. <button id="button3">Button3</button>
  17. </body>
  18. </html>

当文档加载完毕,给几个按钮注册点击事件,当我们点击按钮时,会弹出什么提示框呢?

很容易犯错,对是的,三个按钮都是弹出:"Button4",你答对了吗?

当注册事件结束后,i的值为4,当点击按钮时,事件函数即function(){ alert("Button"+i);}这个匿名函数中没有i,根据作用域链,所以到buttonInit函数中找,此时i的值为4,

所以弹出”button4“。

四:with语句

说到作用域链,不得不说with语句。with语句主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。

看下面代码

[javascript] view plaincopy

  1. person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};
  2. with(person.wife){
  3. console.log(name);
  4. }

with语句将person.wife添加到当前作用域链的头部,所以输出的就是:“lwy".

with语句结束后,作用域链恢复正常。

时间: 2024-11-08 21:50:53

javascript变量声明 及作用域的相关文章

【转】javascript变量声明 及作用域

javascript变量声明提升(hoisting) javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面. 先看一段代码 1 2 3 4 5 var v = "hello"; (function(){   console.log(v);   var v = "world"; })(); 这段代码运行的结果是什么呢?答案是:undefined这段代码说明了两个问题,第一,funct

javascript变量声明和作用域提升

以前的知识总是忘,遇到代码又看不懂.要再复习一下,顺便记录一下. 1 add(1,2); 2 function add(a,b){ 3 alert(a+b); 4 } 代码能输出3,为什么不是按顺序执行?应该是  //add is not defined ... javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面. 也就是说,function add(a,b){alert(a+b)}是一个函数声明,告诉我们有

JavaScript 变量声明提升

JavaScript 变量声明提升 一.变量提升的部分只是变量的声明,赋值语句和可执行的代码逻辑还保持在原地不动 二.在基本的语句(或者说代码块)中(比如:if语句.for语句.while语句.switch语句.for...in语句等),不存在变量声明提升 三.函数声明会提升,但是函数表达式的函数体就不会提升 1 fun(); // hello 2 function fun(){ 3 console.log("hello"); 4 } 5 // -------------- 6 //

javascript变量声明提升

javascript变量声明提升(hoisting) javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面. 先看一段代码 1 2 3 4 5 var v = "hello"; (function(){   console.log(v);   var v = "world"; })(); 这段代码运行的结果是什么呢?答案是:undefined这段代码说明了两个问题,第一,funct

javascript变量声明前置

变量声明前置: 所谓的变量声明前置就是在一个作用域块中,所有的变量都被放在块的开始出声明,下面举个例子你就能明白了 1 var a = 1; 2 function main() { 3 console.log(a);//1 4 } 5 main();//输出1 上面代码输出的是外层变量a的值 1 var a = 1; 2 function main() { 3 console.log(a); 4 var a = 2; 5 } 6 main()//输出undefined 问什么这样就输出undef

Javascript变量声明

Javascript变量声明 JavaScript中即可以使用双引号声明字符串,也可以使用单引号声明字符串.主要是为了方便和html集成,避免转义符的麻烦. JavaScript是动态类型,因此 var i=10;i='test'是合法的. var m,n,x,y=10;也是合法 判断变量是否可用 Javascript中,要用到一个变量计算的时候,首先要判断变量不是undefined也不是null 有两种判断方法,但是有区别,推荐第二种. 第一种: <script type="text/j

javascript变量声明提升(hoisting)

javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面. 先看一段代码 1 2 3 4 5 var v = "hello"; (function(){   console.log(v);   var v = "world"; })(); 这段代码运行的结果是什么呢? 答案是:undefined 这段代码说明了两个问题, 第一,function作用域里的变量v遮盖了上层作用域变量v.代

javascript变量声明的一点感想

相对于C/C++来说,ECMAScript里的for循环并不能创建一个局部的上下文. for (var k in {a: 1, b: 2}) { alert(k); } alert(k); // 尽管循环已经结束但变量k依然在当前作用域 任何时候,变量只能通过使用var关键字才能声明. 上面的赋值语句: a = 10; 这仅仅是给全局对象创建了一个新属性(但它不是变量).“不是变量”并不是说它不能被改变,而是指它不符合ECMAScript规范中的变量概念,所以它“不是变量”(它之所以能成为全局对

javascript变量声明,hoisting机制,面向对象

以下不遵守可能引起未知错误 1.  表示区块起首的花括号,不要另起一行. 2.  不要省略句末的分号 3.  不要使用with语句,可以减少代码的书写,但是会造成混淆. 4.  不要使用"相等"(==)运算符,只使用"严格相等"(===)运算符. == 两边值类型不同的时候,要先进行类型转换,再比较,造成很多意想不到的情况. === 不做类型转换,类型不同的一定不等. 0 == ''// true 1 == true // true 2 == true // fal