`
该用户名已经存在
  • 浏览: 307808 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论
阅读更多
这篇文章中已经简单的提到了函数和变量提升的概念。下面将更加仔细的阐述Javascript中的变量和函数的提升。


一、变量和函数提升的现象
首先思考下面的代码执行后将打印出什么?
var foo = 1;
function bar()
{
	if (!foo) 
	{
		var foo = 10;
	}
	alert(foo);
}
bar();

如果这个结果("10")会让你感到惊讶,那么下面的例子会不会也会让你同样的惊讶:
var a = 1;
function b() 
{
	a = 10;
	return;
	function a() {}
}
b();
alert(a);

没错,浏览器将打印"1"。这里面到底发生了什么?这看起来似乎很奇怪,危险而且很迷惑,
这事实上就是Javascript这门语言强大的、具有表现力的功能。以上的现象没有一个标准的名字,姑且就叫其为“提升”,这篇文章主要就是讲解一下Javascript的这一机制,但是在学习提升之前我们首先要了解一下Javascript的“域”,这样才能更好的理解提升。

二、Javascript的域
对于Javascript的初学者来讲,域是学习Javascript最大的疑惑,事实上很多熟练的Javascript开发人员也不是能完全的理解域,域在Javascript中如此具有迷惑性的原因是它看起来像C族语言,考虑以下的C代码:
#include <stdio.h>
int main() {
	int x = 1;
	printf("%d, ", x); // 1
	if (1) {
		int x = 2;
		printf("%d, ", x); // 2
	}
	printf("%d\n", x); // 1
}

以上代码将依次输出1,2,1,这是因为C以及其他C族语言拥有“块级域”(block-level scope)。当控制语句进入一个块,比如if语句块,我们能在这个块中声明新的变量,而不会影响外部的“域”。但在Javascript中并不是这样的,尝试在FireFox中运行以下代码:
var x = 1;
console.log(x); // 1
if (true) {
	var x = 2;
	console.log(x); // 2
}
console.log(x); // 2

以上FireFox将打印1,2,2,这是因为Javascript拥有“函数级域”(function-level scope),这是和C族语言根本上的不同,像if语句这样的块不能形成一个新的域,只有函数能创建新的域。对于很多曾经喜欢使用C,C++,C#或者Java的程序员来讲,这种现象是不希望也是不受欢迎的。幸运的是由于Javascript函数的灵活性,这一现象是可以得到弥补的,如果非要在一个函数里面再创建一个临时域,看以下例子:
function foo() {
	var x = 1;
	if (x) {
		(function () {
			var x = 2;
			// some other code
		}());
	}
	// x is still 1.
}

这个方法实际上非常的灵活,而且能够在任何你需要使用临时域的地方使用这种方法,不仅仅是在语句块中。强烈建立大家真正的花时间去理解和领会Javascript的域。它非常的强大,如果你理解了Javascript的域和提升,对你非常有意义的。

三、声明,命名和提升
在Javascript中,一个名字在一个域中创建可以通过以下四个方式之一:
1.语言自身的定义:默认所有的域都被给予this和arguments两个命名。
2.正式的参数:函数提供的域中会为该函数参数提供命名。
3.函数声明:函数提供的域会为函数体内声明的其他函数提供命名。
4.变量声明:函数提供的域会为函数体内声明的变量提供命名。
函数声明和变量声明常常会被Javascript解释器隐形的提升到包含它们的域的顶部,这就意味着像一下的代码:
function foo() {
	bar();
	var x = 1;
}

实际上更像是这样被解读的:
function foo() {
	var x;
	bar();
	x = 1;
}

这就说明,我们声明变量或者函数的那一行语句会不会被执行是没有关系的。什么意思呢?就是以下两个函数是等效的:
function foo() {
	if (false) {
		var x = 1;
	}
	return;
	var y = 1;
}
function foo() {
	var x, y;
	if (false) {
		x = 1;
	}
	return;
	y = 1;
}

需要注意的是:变量初始化的部分并没有被提升,只是变量声明的部分被提升。但是对于函数来说有两种形式:函数声明和函数表达式。对于函数表达式是和变量一样的,但对于函数声明,是将整个函数体一起提升的:
function test() {
	foo(); // TypeError "foo is not a function"
	bar(); // "this will run!"
	var foo = function () { // function expression assigned to local variable 'foo'
		alert("this won't run!");
	}
	function bar() { // function declaration, given the name 'bar'
		alert("this will run!");
	}
}
test();

这就是提升的基本原则,没有想象中的那么奇怪和复杂。其实提升的本质还是《Javascript的函数和执行环境》
一文中讲到的Javascript的执行环境的创建问题,提升只是我们看到的直观的现象,如果要深入了解提升的本质,请参考该文。

良好的编码习惯,由于Javascript的变量提升的语言特点,如果我们在函数中到处声明变量,那完全是自讨苦吃,既然我们在哪儿声明变量都会被Javascript解释器隐式的提升到最顶部,我们为什么不直接在函数的最顶部声明我们的变量或者函数,这样既直观,看起来也整洁,也不至于产生混淆,会避免很多不必要的麻烦。
/*jslint onevar: true [...] */
function foo(a, b, c) {
	var x = 1,
		bar,
		baz = "something";
}
分享到:
评论

相关推荐

    【JavaScript源代码】JavaScript中变量提升和函数提升实例详解.docx

    变量提升和函数提升在JavaScript中的应用 JavaScript是一种基于对象的脚本语言,广泛应用于Web开发中。其中,变量提升和函数提升是JavaScript语言中两个非常重要的概念。变量提升和函数提升是JavaScript语言的核心...

    深入理解JavaScript作用域和作用域链

    正确管理作用域可以避免全局变量冲突,提高代码的复用性和模块化,同时也有助于提升性能,因为局部变量的访问速度通常比全局变量快。在实际编程中,我们还会遇到闭包、块级作用域(ES6中的`let`和`const`)等更高级...

    JavaScript作用域原理

    变量提升(Hoisting)是JavaScript中的一个关键特性,它指的是var声明的变量和函数声明会被提升到它们所在的作用域顶部。然而,let和const声明的变量不会被提升,这称为暂时性死区(Temporal Dead Zone, TDZ)。如果...

    深入浅析JavaScript中的作用域和上下文

    JavaScript中,作用域和上下文是理解代码执行逻辑的关键概念。作用域指的是变量和函数的可见性和生命周期,而上下文则关乎`this`关键字的值,它指示了当前代码执行的环境。 **作用域(Scope)** 1. **全局作用域**...

    深入理解JavaScript作用域共12页.pdf.zip

    在JavaScript中,作用域主要有两种类型:全局作用域和局部作用域。全局作用域的变量在整个脚本中都是可访问的,而局部作用域的变量只在其声明的函数或块级作用域内有效。当在函数内部声明变量时,通常会创建局部作用...

    深入理解Javascript作用域与变量提升

    Javascript作用域和变量提升是该语言中的核心概念之一,初学者经常对这些概念感到困惑。在深入讨论这些主题之前,我们需要理解什么是作用域(Scope)和变量提升(Hoisting)。 作用域是指变量在特定区域内是否可见...

    JavaScript 基础函数_深入剖析变量和作用域

    在ES6中,引入了`let`和`const`两个新的关键字,用于声明块级作用域的变量,它们的使用更加严格,不允许变量提升,并且在同一作用域内不允许重复声明,这为JavaScript带来了更多的灵活性和更好的管理变量的能力。...

    JavaScript语言精粹(高清电子版)和高性能JavaScript 双语版

    2. **作用域和闭包**:讲解了如何管理变量的作用域,以及闭包如何为JavaScript提供强大的特性,例如模块化和记忆化。 3. **原型和继承**:JavaScript的面向对象编程特性,如原型链、构造函数和实例化。 4. **异步...

    JavaScript程序设计-变量作用域.pdf

    JavaScript引擎在执行代码之前会进行预解析,这个过程中会将变量和函数的声明提升到它们所在作用域的顶部,但不包括赋值操作。这意味着,即使在变量声明之前使用,也不会报错,但变量的值会是`undefined`。函数声明...

    JavaScript作用域原理(二) 预编译[9 29]

    在JavaScript中,主要有两种作用域:全局作用域和局部作用域。全局作用域通常在函数外部定义,可以在整个代码范围内被访问;而局部作用域则在函数内部定义,仅在其定义的函数内部可见。 JavaScript中的预编译,也...

    JavaScript内核系列 pdf

    理解作用域规则,特别是块级作用域(let和const的引入)和函数作用域,以及如何利用闭包进行数据封装和状态管理,是提升编程技能的关键。 六、异步编程 JavaScript是非阻塞的单线程语言,通过事件循环和回调函数、...

    深化浅析JavaScript中的作用域和上下文_.docx

    在ES6之前,JavaScript没有块级作用域,但ES6引入了`let`关键字,允许在块级结构(如`if`、`for`循环等)中定义变量,这些变量仅在其所在的块内可见,解决了`var`可能导致的一些问题,如变量提升。 **函数作用域**...

    JavaScript中作用域链的概念及用途讲解

    JavaScript中的作用域链是编程中一个至关重要的概念,它决定了变量和函数的可访问性以及在不同作用域内的查找顺序。...因此,花时间学习和理解作用域链的概念及其用途,对于提升JavaScript编程技能是极其有益的。

    JavaScript权威指南(JavaScript犀牛书一本)

    3. **作用域和闭包**:解析了JavaScript中的变量作用域规则以及闭包的概念,这是解决许多复杂问题的基础。 4. **异步编程**:详述了事件循环、回调函数、Promise、async/await等异步处理方式,帮助开发者应对...

    掌握全局代码执行和作用域的提升.doc

    JavaScript的全局代码执行和作用域提升是编程中至关重要的概念,它们直接影响到代码的执行顺序和变量的可访问性。让我们深入探讨一下这些概念。 首先,JavaScript引擎在执行任何代码之前,会进行预处理,也被称为...

    JavaScript简介和特点.rar

    - **作用域**:JavaScript有全局作用域和局部作用域,函数内部定义的变量只在函数内部有效。 2. **控制结构**: - **流程控制**:如条件语句(if...else)、循环(for、while)用于决定程序执行路径。 - **函数*...

    javascript 闭包、匿名函数、作用域链

    JavaScript中的闭包、匿名函数和作用域链是编程中至关重要的概念,它们是理解JavaScript运行机制的关键。在本文中,我们将深入探讨这三个概念,并通过实际示例来展示它们的运用。 首先,我们来讨论“闭包”。闭包是...

Global site tag (gtag.js) - Google Analytics