JavaScript变量作用域和变量提升解释(JavaScript Variable Scope and Hoisting Explained)

翻译 2013年12月04日 15:12:39

原文:JavaScript Variable Scope and Hoisting Explained

这篇文章,我们要学习的是javascript的变量作用域和变量的提升以及它们的特性。

理解javascript的变量作用域和变量提升对于学习javascript来说,是非常重要的。这些概念看起来似乎很直白,但是还是有些很重要的细微之处需要我们理解的。

变量的作用域

变量的作用域就是变量的生存环境。简单的说,变量的作用域就是你在什么地方可以访问到这个变量。

javascript中的变量作用域只用两种:局部作用域和全局作用域。

局部作用域(函数级作用域)

跟其他大多数编程语言不同的是,javascript没有块级作用域(比如java中的if语句块中声明的变量,作用域就是这个if语句块,但是javascript中,if语句块中的变量却是全局变量)。不过,javascript有函数级作用域。在一个函数内定义的变量,叫做局部变量。它只能在这个函数里或者这个函数的内部函数里被访问到。更多关于内部函数访问外部函数中定义的变量的内容,请参考Closures(闭包)这篇文章。

函数级作用域演示:

var name = "Richard";

function showName () {
	var name = "Jack"; // 局部变量; 只能在 showName 函数中被访问
	console.log (name); // Jack
}
console.log (name); // Richard: 全局变量
javascript中没有块级作用域,这是跟其他大部分编程语言都不一样的地方,需要特别注意

var name = "Richard";
// 这个if语句块没有生成一个新的环境
if (name) {
	name = "Jack"; // 这个name是全局变量name,它的值被改成了"Jack"
	console.log (name); // Jack: 这个还是全局变量
}

// 这里的name,还是全局变量,但是它在if语句块中被改变了值。
console.log (name); // Jack

  • 如果你不声明局部变量的话,很容易出错
    在使用局部变量之前,一定要记得先声明。你可以用JSHint 来检查你的语法。下面这个例子是不声明本地变量导致的问题:
    //即使是在函数里面,声明一个变量时,没有使用var这个关键字,那这个变量依然是全局变量。
    var name = "Michael Jackson";
    
    function showCelebrityName () {
    	console.log (name);
    }
    
    function showOrdinaryPersonName () {	
    	name = "Johnny Evers";
    	console.log (name);
    }
    showCelebrityName (); // Michael Jackson
    
    //因为在函数里面没有对name进行声明,所以它用到的name是全局变量里面的name,只是把它的值改变成了"Johnny Evers"
    showOrdinaryPersonName (); // Johnny Evers
    
    // 全局变量name的值现在是Johnny Evers了,而不再是原来的"Michael Jackson"了。
    showCelebrityName (); // Johnny Evers
    
    // 下面是正确的在函数里声明本地变量的方法
    function showOrdinaryPersonName () {	
    	var name = "Johnny Evers"; // 现在这个name是本地变量了,不会覆盖掉全局变量里面的name.
    	console.log (name);
    }
  • 在函数里局部变量的优先权高于全局变量
    如果你同时声明了一个全局变量,一个局部变量,两者的名字一样的话,在函数里面使用这个变量的时候,将优先使用本地声明的这个变量。在一个函数里面使用一个变量的时候,它会首先在本地环境里面查找这个变量,找到了就直接使用,只有在本地找不到该变量时,它才会到全局环境里去找全局变量。
    var name = "Paul";
    
    function users () {
    	// 这里声明了一个局部变量,跟全局变量的变量名都是name,但是在函数里,它的优先级比全局变量高
    var name = "Jack";
    
    // 首先在这个函数里面查找name变量,找不到的话,才会去查找函数外的全局变量。
    console.log (name); 
    }
    
    users (); // Jack

全局变量
所有在函数之外声明的变量都是全局变量。在浏览器里面,全局环境或者说全局作用域的作用范围是window对象(或者是整个html文档)

  • 所有在函数外面声明或者初始化的变量都是全局变量,全局变量在整个应用中任何地方都可以被使用。比如:
    // 下面几种都是声明全局变量的方式
    var myName = "Richard";
    
    // 或者
    firstName = "Richard";
    
    // 或者
    var name;
    name;
    
    
    //需要注意的是,所有全局变量都跟window对象是关联上的。所有我们声明的全局变量可以像这样访问:
    
    console.log(window.myName); // Richard;
    
     // 或者
    console.log("myName" in window); // true
    console.log("firstName" in window); // true
  • 如果变量初始化(赋值)之前,没有使用var 关键字进行声明,那这个变量自动被加到全局环境里面,变成全局变量了。
    function showAge () {
    	// 这个age是全局变量
    	age = 90;
    	console.log(age);// 
    }
    
    showAge (); // 90
    
    // 因为age是全局变量,所以在函数外也能被访问到。
    console.log(age); // 90

    另一个例子:

    // 尽管第二个firstName被包含在{}中,这两个都是全局变量。因为javascript中没有块级作用域这一说。
    var firstName = "Richard";
    {
    var firstName = "Bob";
    }
    
    // 第二个firstName只是又声明了一次并且覆盖了第一个firstName的值
    console.log (firstName); // Bob

    另一个例子:

    for (var i = 1; i <= 10; i++) {
    	console.log (i); // 输出 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;
    };
    
    // i是全局变量,所以下面的函数里面可以访问到。
    function aNumber () {
    console.log(i);
    }
    
    // aNumber函数里面的i是全局变量,它的值在上面的for循环里面被改变了,最后的值是for循环结束前的11.
    aNumber ();  // 11

  • setTimeout 的变量会被放在全局域里执行
    注意所有在setTimeout里面的函数都会在全局域里面执行。 这是比较棘手的一点,看下面这个:
    // 在setTimeout里面的 "this" 对象表示的是 window 对象,而不是 myObj
    
    var highValue = 200;
    var constantVal = 2;
    var myObj = {
    	highValue: 20,
    	constantVal: 5,
    	calculateIt: function () {
     setTimeout (function  () {
    	console.log(this.constantVal * this.highValue);
    }, 2000);
    	}
    }
    
    //setTimeout 函数里面的 "this" 对象使用全局的变量 highValue 和 constantVal,因为setTimeout函数里面的 "this" 的引用时全局的window对象,不是myObj
    
    myObj.calculateIt(); // 400
    // This is an important point to remember.
  • 不要过度使用全局变量
    如果你想成为一个javascript高手的话,这个应该是毫无疑问的(否则你现在应该在看选美小姐什么的而不是在读这篇文章了),你一定要尽量避免在全局域中创建过多的变量,像下面这样:
    // 这两个变量现在在全局域里面,但是更好的方法是把它们放到函数里面
    var firstName, lastName;
    
    function fullName () {
    	console.log ("Full Name: " + firstName + " " + lastName );
    }

    下面这个是改良之后的版本

    // 在函数里面声明变量,它们就成了局部变量了。
    
    function fullName () {
    	var firstName = "Michael", lastName = "Jackson";
    
    	console.log ("Full Name: " + firstName + " " + lastName );
    }

    这上面这个例子中,函数fullName依然是在全局域中。


变量提升

在javascript中,有个变量声明提升的概念。在一个函数里声明的变量,它的声明会被提到函数的顶部。在函数外定义的变量,它的声明则会被提到全局环境的顶部。

需要强调的是,只有函数的声明会被提升,变量的初始化或者赋值都不会被提升。

变量提升的例子:

function showName () {
console.log ("First Name: " + name);
var name = "Ford";
console.log ("Last Name: " + name);
}

showName (); 
// First Name: undefined
// Last Name: Ford

// 第一行打印出来的是undefined的原因就是局部变量name被提升到了函数的顶部。
// 第一行的name调用的就是这个name
// 下面的代码是javascript引擎实际的执行过程:

function showName () {
	var name; // name的声明被提升到函数的顶部,由于赋值不会被提升,所以此时name的值为undefined
console.log ("First Name: " + name); // First Name: undefined

name = "Ford"; // name在原来这个地方被赋值

// 现在name的值变为了Ford
console.log ("Last Name: " + name); // Last Name: Ford
}

在提升的时候函数声明覆盖变量声明
函数的声明和变量的声明都会被提升到所在作用域的顶部。如果函数的名字和变量的名字相同的话,函数的声明会覆盖掉变量的声明(但是不能覆盖变量的赋值)。像上面提到的,变量的赋值是不会被提升的,函数的赋值也不会被提升。附:函数的赋值的格式 var myFunction = function () {}。
下面是一个基本的演示例子:

 // 函数和变量的名字都是 myName
var myName;

function myName () {
console.log ("Rich");
}

//函数的声明覆盖了变量的名字
console.log(typeof myName); // function

 // 但是在这个例子中,变量的赋值又覆盖了函数的声明
var myName = "Richard"; // 变量赋值(初始化)覆盖函数的声明

function myName () {
console.log ("Rich");
}

console.log(typeof myName); // string 

值得注意的是,像下面这样的函数表达式,是不会被提升的。

var myName = function () {
console.log ("Rich");
} 

在strict 模式下,如果你给一个没有被声明的变量赋值,会产生错误。所以一定在变量赋值之前要先声明变量。

javascript中函数作用域之”提升“

javascript中函数作用域之变量提升        当我们在函数内部用关键字var声明一个变量的时候,此变量的作用域限制在当前函数。        提升:在一个作用域内部,不管一...
  • doctor_who2004
  • doctor_who2004
  • 2015年08月31日 21:51
  • 690

关于JavaScript变量提升的理解

JavaScript中常听见的名词。变量提升,感觉好高大上的样子,其实也没多神秘。有可能是我不求甚解。 中心概念就是:在某一作用域中,声明变量的语句会默认解析为在该作用域的最开始就已经声明了。...
  • cc2293260
  • cc2293260
  • 2016年04月19日 16:08
  • 1334

理解JavaScript的变量,变量作用域,作用域链

JavaScript的变量,变量作用域,作用域链
  • ganyingxie123456
  • ganyingxie123456
  • 2017年03月05日 22:34
  • 393

JavaScript 中的执行环境、作用域(scope)以及变量提升(hoisting)

先看下面一段代码: var a = 0; alert("1st alert : a = " + a); function fun(){ alert("2nd alert : a = " + ...
  • cy012124
  • cy012124
  • 2014年09月22日 12:19
  • 999

深入理解JS中的变量作用域

文章转载http://blog.csdn.net/beijiguangyong/article/details/8301707点击打开链接 在JS当中一个变量的作用域(scope)是程序中定义这个变...
  • bbs375
  • bbs375
  • 2016年09月09日 08:45
  • 2059

js变量作用域--变量提升

js的变量和其他语言的变量作用域,有很多相同的地方,但也有很多不相同的地方。尤其是变量提升这个概念。之前还真没听说过这个概念。   先看具体的例子: function test2(){ ale...
  • xuexiaodong2009
  • xuexiaodong2009
  • 2015年05月27日 15:02
  • 1358

深入理解变量声明提升和函数声明提升

变量声明提升1、变量定义可以使用var定义变量,变量如果没有赋值,那变量的初始值为undefined。2、变量作用域变量作用域指变量起作用的范围。变量分为全局变量和局部变量。全局变量在全局都拥有定义;...
  • qq673318522
  • qq673318522
  • 2016年03月05日 20:51
  • 9269

JavaScript变量提升(Hoisting)

Scoping & Hoisting var a = 1; function foo() { if (!a) { var a = 2; } alert(a);...
  • shilu89757
  • shilu89757
  • 2015年01月06日 17:27
  • 568

JavaScript的那些坑之变量提升

介绍了JavaScript的变量提升特性和var的一些注意事项。
  • qq_25936689
  • qq_25936689
  • 2016年01月06日 21:10
  • 1150

简要描述 JavaScript 中的作用域链

任何一段JavaScript代码都对应一个作用域链,作用域链中存放一系列对象,代码中声明的变量将作为对象的属性存放。在JavaScript的顶层代码中,作用域链由一个全局对象组成;当定义一个函数时,它...
  • u012396955
  • u012396955
  • 2016年12月29日 17:18
  • 226
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:JavaScript变量作用域和变量提升解释(JavaScript Variable Scope and Hoisting Explained)
举报原因:
原因补充:

(最多只允许输入30个字)