西部数码主机 | 阿里云主机| 虚拟主机 | 服务器 | 返回乐道官网
当前位置: 主页 > 开发教程 > JavaScript教程 >

JavaScript的作用域与闭包

时间:2016-02-05 16:33来源:未知 作者:好模板 点击:
JavaScript语言提供了函数作用域,同时JavaScript允许嵌套的函数定义。 有趣的是,内部函数(inner function)的生命周期可能超过父级函数。 这时便会显示出JavaScript的一个特殊现象:闭包。

JavaScript语言提供了函数作用域,同时JavaScript允许嵌套的函数定义。 有趣的是,内部函数(inner function)的生命周期可能超过父级函数。 这时便会显示出JavaScript的一个特殊现象:闭包。 闭包为面向对象编程提供了另外一种有趣的封装方式,我们无需为私有变量声明 private 。

函数作用域

程序设计语言中的 作用域 (scope)控制着变量和参数的可见性和生命周期。 它的重要性体现在避免命名冲突和自动内存管理两个方面,极大地减少了程序员的工作。

多数编程语言都拥有 块作用域 (block scope),由一对大括号限定其中变量的作用域。 比如C++的变量只在块内可见,变量被定义时执行内存申请和构造函数, 控制流退出代码块后内部的变量又被析构和内存回收。

不幸的是JavaScript提供了块语法,却不提供块作用域,而是提供函数作用域。 这意味着参数和变量在函数外部不可见,在函数内部始终可见。

闭包

JavaScript允许在函数中嵌套定义函数,这意味着 内部函数(inner function)也可以访问父级函数的上下文(包括参数和变量) 。 有趣的是,有时内部函数拥有更长的生命周期。 来一个典型的面试题:

function f(){
    for(var i=0; i<3, i++){
        setTimeout(function(){
            console.log(i);
        }, 1000);
    }
}

输出:

i 是 function f 中定义的变量,在传递给 setTimeout 的函数中, i 仍然有效(其实它叫做 闭包变量 )。 所以1s后 console.log 时 i 仍然是 function f 中定义的那个 i ,这时它的值为 3 。

这意味着内部函数可以访问真正的外部变量,而不是外部变量的副本。 函数可以访问它被创建时的所有上下文变量,这就叫做 闭包现象 。

如果我们希望上述代码输出 123 ,我们需要开启一个子作用域。而JavaScript只提供函数作用域,所以我们需要添加一层函数:

function f(){
    for(var i=0; i<3, i++){
        !function(i){
            setTimeout(function(){
                console.log(i);
            }, 1000);
        }(i);
    }
}

这时输出便是 123 了。其中的 ! 是为了让JavaScript将这一句解析为表达式,而不是函数声明。 其实任何运算符都可以( +- ),但如果没有便是语法错。

变量提升

一般编程语言建议将变量定义推迟,越晚越好。然而在JavaScript中不是这样, 应当将一切变量都在函数体最上方声明。例如:

var p = {name: 'harttle'};
function foo(){
    console.log(p.name);
    var p = {name: 'alice'};
}

上述代码将产生运行时错误:

Uncaught TypeError: Cannot read property 'name' of undefined

这是因为在JavaScript的函数作用域实现中,所有变量声明( var xxx )都会被提升。 就像在函数体刚进入时声明这些变量一样。上述代码相当于:

var p = {name: 'harttle'};
function foo(){
    var p;
    console.log(p.name);
    p = {name: 'alice'};
}

Note:变量声明与初始化是两回事,变量提升指的是声明提升,初始化/赋值并不会提升。

闭包封装

与基于类声明的面向对象语言不通,JavaScript基于原型继承。 JavaScript并未提供public , protected , private 等关键字。 而JavaScript的闭包机制则可以完美地提供封装。你只需要:

  1. 将public属性赋值到对象属性。
  2. 将private属性声明为局部变量或内部函数。

例如:

function Counter(){
    var i = 0;
    return {
        count: function(){ i++; }
        get: function(){ return i; }
    }
}
var counter = Counter();
counter.count();
counter.get();      // 1

其中 i 便是 private , count 与 get 便是 public 。 这里JavaScript有一个设计错误:调用内部函数时 this 不会正确传递。

(责任编辑:好模板)
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
栏目列表
热点内容