JavaScript 的closure 和 hoisting

闭包(closure)

当声明一个函数时,其内部的变量的声明也在它的scope中被截获。比如下面的代码中,变量 x 绑定到了外部scope的值,然后对 x 的引用在bar的上下文中被截获。

var x = 4; // declaration in outer scope

function bar() {
    console.log(x); // outer scope is captured on declaration
}

bar(); // prints 4 to console

“截获”的概念很有意思,因为我们可以从外层scope使用和修改变量,甚至外层scope已经不存在了,比如:

function foo() {
    var x = 4; // declaration in outer scope

    function bar() {
        console.log(x); // outer scope is captured on declaration
    }

    return bar;

    // x goes out of scope after foo returns
}

var barWithX = foo();
barWithX(); // we can still access x

上面的例子中,当foo() 被调用时,其上下文在函数 bar() 中被截获。所以在它返回之后, bar() 仍然可以访问和修改变量 x。函数 foo(),其上下文在另一个函数中被截获,叫做闭包。

  • 私有数据

    让我们做一些有意思的事,比如定义private变量,让它只对特定的函数可见:

function makeCounter() {
    var counter = 0;

    return {
        value: function () {
            return counter;
        },
        increment: function () {
            counter++;
        }
    };
}

var a = makeCounter();
var b = makeCounter();

a.increment();

console.log(a.value());
console.log(b.value());

输出

1
0

当 makeCounter() 被调用时,这个函数的快照会被保存。所有其内部的代码在执行过程中都使用这份快照。两次调用创建两个不同的快照,每个快照都有自己的counter变量。

  • IIFE

    闭包也用于防止全局全名空间的污染,通常通过IIFE做到这一点。

    IIFE是特殊的闭包,在声明之后立即调用。

    假设我们想用 “$” 引用 jQuery,考虑下面的方法,不用IIFE。

var $ = jQuery;
// we‘ve just polluted the global namespace by assigning window.$ to jQuery

下面的例子中,IIFE用于确保 “$” 绑定到了闭包创建的 jQuery。

(function ($) {
    // $ is assigned to jQuery here
})(jQuery);
// but window.$ binding doesn‘t exist, so no pollution

Hoisting

Hoisting是这样一个机制,把所有变量和函数的声明移动到它们的scope的顶部,但是,赋值仍然发生在声明的地方。比如:

    console.log(foo);
    // - > undefined
    var foo = 42;
    console.log(foo);
    // - > 42

上面的代码相当于:

    var foo; // Hoisted variable declaration
    console.log(foo);
    // - > undefined
    foo = 42; // variable assignment remains in the same place
    console.log(foo);
    // - > 42

注意因为上面的undefined 与运行下面代码得到的 not defined 不同:

    console.log(foo);
    // - > foo is not defined 

类似的理念也适用于函数。当函数被赋值给一个变量时,变量的声明就被hoisted了,而赋值还发生在原处。下面的两块代码是一样的:

    console.log(foo(2, 3));
    // - > foo is not a function
    var foo = function(a, b) {
        return a * b;
    }
    var foo;
    console.log(foo(2, 3));
    // - > foo is not a function
    foo = function(a, b) {
        return a * b;
    }

当声明function statements时,是不同的场景,不像function statements,函数的声明在它自己的scope内被hoisted,看下面的代码:

    console.log(foo(2, 3));
    // - > 6
    function foo(a, b) {
        return a * b;
    }

上面的代码与下面的相同:

    function foo(a, b) {
        return a * b;
    }
    console.log(foo(2, 3));
    // - > 6

下面的例子解释了hoisting是什么,hoisting不是什么

// (A) valid code:
foo();

function foo() {}

// (B) **invalid**:
bar(); TypeError: bar is not a function
var bar = function () {};

// (C) valid:
foo();
function foo() {
    bar();
}
function bar() {}

// (D) **invalid**:
foo();
function foo() {
    bar(); // TypeError: bar is not a function
}
var bar = function () {};

// (E) valid:
function foo() {
    bar();
}
var bar = function(){};
foo();
时间: 2024-10-09 21:51:49

JavaScript 的closure 和 hoisting的相关文章

javascript 闭包(closure)

<script type="text/javascript">    //闭包(closure):内层函数可以引用存在于包围它的函数内的变量,即使外层函数的执行已经结束    //注意内层函数引用的外层函数内的变量是外层函数执行结束后的最终值    test(1);    function test(a) { //外层函数        alert(a+' 外层函数开始执行');        setTimeout(function(){//内层函数           

深入理解JavaScript闭包(closure)

最近在网上查阅了不少Javascript闭包(closure)相关的资料,写的大多是非常的学术和专业.对于初学者来说别说理解闭包了,就连文字叙述都很难看懂.撰写此文的目的就是用最通俗的文字揭开Javascript闭包的真实面目. 一.什么是闭包? “官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话通俗的来说就是:JavaScript中所有的function都是一个

Javascript中Closure及其相关概念

我相信学过Javascript这门语言的程序员应该都对Closure这个概念有所了解,然而网上以及各种Javascript书籍里面对Closure这个概念的定义有各种说法.我本人觉得很多地方对Closure这个概念的定义都是片面的,目前看到的比较全面准确的定义应该是Wikipedia上面的定义了,但是Wikipedia上面的定义不是很好理解. 我通过网上查阅了些资料后结合Wikipedia的定义,下面给出我自己对Closure这个概念的理解. 要想正确理解闭包必须要先对一些概念有所了解: 非本地

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变量声明,hoisting机制,面向对象

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

JavaScript中变量提升------Hoisting

本文转自 damonlan的文章 http://www.cnblogs.com/damonlan/archive/2012/07/01/2553425.html 前言 因为我在写这文章的时候,百度里找资料,找到了园友的一篇文章,写的很好,可是我写了又不想放弃,所以就在里面拿了很多东西过来!~~ [翻译]JavaScript Scoping and Hoisting 希望得到大家谅解. 因为这个问题很是经典,而且容易出错,所以在介绍一次.哈哈.莫怪哦. 一.案发现场 我们先看一段很简单的代码: v

JavaScript闭包(closure)

闭包其实就是利用了函数作用域和匿名函数的知识,当函数A执行结束时,一部分变量变量被B引用,被引用的变量不能释放,形成了所谓的闭包.这里有篇很好的文章,可以参考一下. 下面看一个小例子: JavaScript 1 2 3 4 5 6 7 8 9 10 function show(){ var n=3; setTimeout(function(){alert("first:"+n);},3000); //3秒后显示first:3 alert("second:"+n);

Effective JavaScript Item 12 理解Variable Hoisting

本系列作为Effective JavaScript的读书笔记. JavaScript中并没有Block Scoping,只有Function Scoping. 因此如果在一个Block中定义了一个变量,那么这个变量相当于是被定义到了这个Block属于的Function中,比如: function isWinner(player, others) { var highest = 0; for (var i = 0, n = others.length; i < n; i++) { <span s

JAVASCRIPT的一些知识点梳理

春节闲点,可以安心的梳理一下以前不是很清楚的东东.. 看的是以下几个URL: http://web.jobbole.com/82520/ http://blog.csdn.net/luoweifu/article/details/41466537 http://javascriptissexy.com/understand-javascript-closures-with-ease/ http://javascriptissexy.com/javascript-variable-scope-an