JavaScript中的函数作用域\函数中的作用域 (作用域与闭包 - Ch3.1)

函数声明 - 创建作用域气泡

首先观察下面的代码:

function foo(a){
    var b = 2;
    function bar(){
        // do something
    }
}

在这个代码片段中,foo(a) 创建了一个作用域气泡,其中包含了标识符a和bar,在全局环境中是无法访问到a和bar的。但在整个函数的范围内,属于整个函数的全部变量都可以任意使用/复用。

封装技术 - 隐藏内部实现

这是为什么要设计变量作用域的一个关键点。

对函数的传统认识就是,先声明一个函数,再往里面塞代码;那么从另一个角度看,代码被提炼出来打包到一个函数里,这就是最小特权原则,或者叫最小暴露原则,在软件设计中最小限度地暴露必要内容,将其他内容隐藏起来,这是模块或者API的设计基础。

那么隐藏其内部内容就是技术关键了,由于JavaScript中常遇到类型变化,需要灵活的变量复用能力,一层层作用域内部嵌套成为了较好的设计。

function doSomething(a){
    b = a + doSomethingElse(a * 2)
    console.log(b * 3)
}

function doSomethingElse(a){ // danger!!!
    return a - 1;   
}

var b; // danger!!!
doSomething(2);

像这里,暴露出doSomethingElse和 变量b是一个糟糕的选择,它们可能会被有意或者无意地以非预期的方式使用,较好的做法是把他们放进doSomething(a) 里面去。

function doSomething(a){
    function doSomethingElse(b){
        return a - 1;
    }
    var b;
    b = a + doSomethingElse(a * 2);
    console.log(b); 
}

避免冲突 - 第三方库作用域

对象命名空间

当程序加载了很多第三方库,如果他们没有将自己的函数或者变量隐藏起来,就容易产生冲突。

所以这些库在全局作用域中声明一个足够独特的变量,通常是一个对象,被叫做库的命名空间,所有功能都成为这个对象的属性,而不是直接暴露出来。

var MyReallyCoolLibrary = {
    awesome: "stuff";
    doSomething: function(){
        // do something
    }
}

模块管理

另一种方法和现代的模块机制很像,使用模块管理器,任何库都不需要将标识符加入到全局作用域,而是通过库的机制进行模块管理。它们只是利用作用域的规则来避免冲突。

匿名函数 - 函数表达式&函数声明

当你需要声明一个函数并进行操作,使用下面的代码:

(function foo(){
    var a = 3;
    console.log(a);
})();

此时foo() 函数将不会污染全局环境,而且随着代码块立即执行,当function作为开头就是函数声明否则为表达式,此时的 (function...就是一个函数表达式。

这样的匿名函数可能有需要考虑的缺点:

  • 匿名函数在栈追踪中可能导致调试困难;

  • 当函数需要引用自身时只能使用已经过期的arguments.callee引用

  • (可避免的) 省略掉了标识符带来的可读性/可理解性

猜你喜欢

转载自blog.csdn.net/Littlelumos/article/details/128721264