我们最常见的作用域有两种,分别为全局作用域与函数作用域。

全局作用域

全局作用域中声明的变量与函数,可以在代码的任何地方被访问。

一般来说,以下三种情形拥有全局作用域。

  • 全局对象下的拥有的属性与方法。
window.name
window.location
window.top
...
  • 在最外层声明的变量与方法。

我们知道全局上下文中的变量对象实际上就是window对象本身,因此我们在全局上下文中声明的变量与方法其实也变成了window的属性与方法,因此他们也拥有全局作用域。

var foo = function() {}
var str = 'out variable';
var arr = [1, 2, 3];
function bar() {}

...
  • 在非严格模式下,函数作用域中未定义但直接复制的变量与方法。

在非严格模式下,这样的变量与方法会自动变成全局对象window的属性。因此他们也会有用全局作用域。

function foo() {
  bar = 20;
}

function fn() {
  foo();
  return bar + 30;
}

fn(); // 50

在实践中,无论是从避免多人协作带来的冲突的角度考虑,还是从性能优化的角度考虑,我们都要尽量少的自定义全局变量与方法。

函数作用域

函数作用域中声明的变量与方法,只能被下层子作用域访问。而不能被其他不相干的作用域访问。

function foo() {
    var a = 20;
    var b = 30;
}
foo();

function bar() {
    return a + b;
}

bar(); // 因为作用域的限制,bar中无法访问到变量a,b,因此执行报错
function foo() {
    var a = 20;
    var b = 30;

    function bar() {
        return a + b;
    }
    return bar();
}
foo();  // 50  bar中的作用域为foo的自作用域,因此能访问到变量a, b

在ES6以前,ECMAScript没有块级作用域,因此使用时需要特别注意,一定是在函数环境中才能生成新的作用域。如下情况则不会有作用域的限制。

var arr = [1, 2, 3, 4, 5];

for(var i = 0; i < arr.length; i++) {
    console.log('do something by ', i);
}

console.log(i); // i == 5

因为没有块级作用域,因此单独的`{ }`并不会产生新的作用域。这个时候i的值会保留下来,在for循环结束之后仍然能够访问。

模拟块级作用域

没有块级作用域会给我们的开发带来一些困扰。例如上面for循环的例子中,i值仍然在作用域中可以被访问,那么这个值就会对作用域中的其他同名的变量造成干扰。因此我们需要想办法模拟块级作用域。而我们刚好知道,一个函数可以生成一个作用域,因此这个时候,我们可以利用函数来达到我们的目的。

var arr = [1, 2, 3, 4, 5];

(function() {
    for(var i = 0; i < arr.length; i++) {
        console.log('do something by ', i);
    }
})();


console.log(i); // i is not defined

这种方式叫做函数自执行。

通过这种方式,我们就可以限定变量i值仅仅只在for循环中生效而不会对其他代码造成干扰。

函数自执行时声明一个匿名函数并且立即执行,这种方式大概有如下几种写法。

(function() {
    ...
})(); // 最常用

+function() {
    ...
}();

-function() {
    ...
}();

!function() {
    ...
}();

当我们使用ECMAScript5时,往往通过函数自执行的方式来实现模块化。而模块化是我们实际开发中需要重点掌握的开发思维,我们会在后续的章节中学习到相关的思路。

results matching ""

    No results matching ""