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 模式下,如果你给一个没有被声明的变量赋值,会产生错误。所以一定在变量赋值之前要先声明变量。

相关文章推荐

[从jQuery看JavaScript]-变量与作用域链(Variable and Scope Chain)

转载:http://blog.csdn.net/natineprince/article/details/4775008

Reduce Scope of Variable -- 缩小变量作用域

Reduce Scope of Variable Refactoring contributed by Mats Henricson You have a local variable dec...

javascript变量作用域

  • 2013-07-25 20:48
  • 18KB
  • 下载

javascript 作用域与变量提升

作用域和变量提升的问题作为javascript的基础也是重点,在笔试面试中也会经常被问到,以下便来总结以下。作用域首先要明确javascript的作用域,第一点要记住的就是javascript没有块级...

Javascript作用域和变量提升

转载来自: http://blog.csdn.net/sunxing007 下面的程序是什么结果? [javascript] view plaincopy ...

JavaScript变量提升(Hoisting)

Scoping & Hoisting var a = 1; function foo() { if (!a) { var a = 2; } alert(a);...

javascript变量声明提升(hoisting)

javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面。 先看一段代码 var v = "hello"; (fu...

JavaScript变量提升-Hoisting

JavaScript中变量提升——Hoisting
  • sampx
  • sampx
  • 2015-07-31 14:37
  • 104

JavaScript中变量提升 Hoisting

一。案发现场  我们先看一段很简单的代码:  复制代码代码如下: var v='Hello World';  alert(v);  这个没有疑问吧,弹出“Hello World”。OK,我...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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