关于 JavaScript 作用域您需要知道的一切






4.50/5 (4投票s)
关于 JavaScript 作用域的示例和解释。
引言
如今,在线应用程序越来越多,正如我们所知,JavaScript 对于任何 Web 应用程序都至关重要,无论是简单的还是富客户端应用程序(RIA)。理解 JavaScript 变得前所未有的重要,而 JavaScript 作用域是编写面向主要从事 Web 应用程序的软件专业人员的“防弹”代码的关键。
在本文中,我将详细介绍您应该了解的 JavaScript 作用域知识。我们将针对每个主题提供示例。
- 范围
- 全局作用域
- 局部作用域
- 函数作用域
- 词法/静态作用域
- 闭包
- “this”对象和作用域
- 使用 call() 更改作用域
- 公共和私有作用域
范围
作用域是指代码的当前上下文。作用域可以是全局的,也可以是局部的。通过理解作用域,您将了解变量和函数/对象可以在哪里访问,并且您将能够更改代码的作用域。它使我们能够编写更易于维护的代码,并且能够更快地进行调试。
全局作用域
当我们开始编写任何 JavaScript 代码时,我们就处于全局作用域中。例如,当我们声明一个变量时,该变量就是全局定义的,这意味着它在脚本中的任何位置都可以访问。
示例
        var app = 'Demo';
        console.log(app);
        function funcA() {
            console.log("inside a function: ", app);
        };
        funcA();
解释
在上面的示例中,变量 app 是在全局作用域中定义的。正如您所看到的,该变量在全局作用域中被调用,也在函数内部被调用。因为变量是在全局级别定义的,所以它在脚本中的任何地方都可以访问。通常只有一个全局作用域。
控制台输出是

局部作用域
当您在全局作用域(顶层)或另一个函数(局部作用域)内定义一个函数时,该函数就有自己的局部作用域。外部函数的局部作用域可以被内部函数访问。
示例 1
  var globalVar = "Global Scope";
  var funcG = function () {
        // Local scope for funcG()
         var funcName = 'funcG';
         console.log(funcName);
         //Local scope can access the global scope
         console.log(globalVar);
        };
    funcG();
   
    //Global scope cannot access the local scope.
    console.log(funcName);
解释
以上示例 1 的输出如下,您可以看到变量 funcName 在最后一行 console.log(funcName); 中是未定义的,因为它是在函数内的局部作用域中定义的,而全局变量 globalVar 可以在局部作用域内的 console.log(globalVar); 中访问。

示例 2
      function funcOuter() {
            var outerVar = 'local variable funcOuter();';
            function funcInner(){
                var innerVar = 'local variable funcInner()';
                console.log('inside funcInner : ', innerVar);
                console.log('inside funcInner : ', outerVar);
            }
            console.log(outerVar);
            console.log(innerVar);
        }
解释
以下是示例 2 的输出,其中 innerVar 是未定义的,因为它定义在 funcInner() 函数内部以及局部作用域内,外部函数无法访问。
同时,在 funcOuter 函数中定义的变量 outerVar 可以在 funcInner() 内部访问,因为外部局部作用域可以在内部局部作用域中访问。

函数作用域
作用域不是为每个语句创建的,而是为每个函数创建的。当使用 FOR 或 WHILE 语句时,不会创建新的作用域。函数即新作用域,这是基本规则。
示例
function funcLoop() {
            for (i = 0; i < 5; i++) {
                console.log('inside loop :', i);
            }
            console.log('outside loop  :', i);
}
funcLoop();
解释
以上代码的输出如下,您可以看到变量 i 是在 FOR 语句内部定义的,并且在循环外部也可以访问,这证明了语句内部没有作用域。

词法/静态作用域
当您在一个函数内定义另一个函数时,内部函数可以访问外部函数的作用域。这称为词法作用域或静态作用域。
示例 1
     var parentFunc = function () {
            var param1 = 'parameter 1';
            var childFunc = function () {
                console.log('The parameter is :', param1);
            }
            childFunc();
            console.log(param1);
        }
        parentFunc();
解释
上面代码的输出如下所示。您可以看到 childFunc() 可以访问定义在外部函数中的 var param1。
词法作用域是从最外层函数到最内层函数工作的。它不会向后工作。

示例 2
function outerMost() {
            console.log(innerMost);
            function outer() {
                console.log(innerMost);
                function inner() {
                    console.log(innerMost);
                    function innerMost() {
                        var innerMost = 'inner most';
                    }
                }
            }
        }
outerMost();
解释
在示例 2 中,var<code> innerMost 定义在 innerMost() 函数中,并且外部函数无法访问它。输出如下所示

闭包
闭包是一个返回两样东西的对象:一个函数和一个与之相关的环境。
闭包与词法作用域紧密相连。我们可以返回作用域内的内容,使其在父作用域中可用。
我们可以通过以下示例来演示。
示例
  var echo = function (shout) {
            var txt = 'Echoing back : ' + shout;
            return function () {
                console.log(txt);
            }
  }
  var f1 = echo('Hello');
  f1();
解释
上面的示例中的函数 echo() 返回一个使用 var txt 显示文本的函数。txt 定义在 echo() 函数内部。然而,正如您从以下输出中看到的,echo() 函数连同 var txt 的环境(作用域)一起被返回。

“this”对象和作用域
关键字‘this’是 JavaScript 编程中非常重要的部分,理解‘this’的值在代码中的不同位置是如何被赋值的,以及函数是如何被调用的,对于编写无错误的代码至关重要。
在最顶层的‘this’的值是 window 对象。然而,‘this’的值会因为以不同的方式调用函数而绑定到不同的值。我们可以通过一个示例轻松演示。
示例
var myFunc = function () {
            console.log(this); 
};
myFunc();
var myObj = {};
myObj.method1 = function () {
           console.log(this);
};
myObj.method1();
<p class='scope'>Click</p>
<script type="text/javascript">
       var p = document.querySelector('.scope');
        var display = function () {
            console.log(this);
        };
p.addEventListener('click', display, false);
解释
第一个函数 myFunc() 返回 window 对象作为‘this’,如下面的输出所示。

第二个函数 myObj.method1() 返回 myObj 对象作为‘this’,因为 method1() 函数是 myObj 对象的一部分。输出如下所示

第三,display() 函数返回 <p> 元素作为‘this’,因为 click() 函数是在 <p> 元素上调用的。输出如下所示

使用 call() 更改作用域
‘this’的作用域可以使用我们代码中的 call() 函数来更改。
我们来看下面的例子
     <ul>
        <li>Red</li>
        <li>Green</li>
        <li>Yellow</li>
        <li>Blue</li>
     </ul>    
     <script type="text/javascript">
         var listItems = document.querySelectorAll('ul li');
         for (var l = 0; l < listItems.length; l++) {
             console.log(this);
         }
     </script>
在‘for’循环中,开发人员试图记录列表项的值。但实际上在控制台记录的值是 window 对象,因为在作用域中‘this’指向 window 对象。控制台输出和浏览器输出如下

当然,我们可以像下面这样通过数组索引来获取列表项的值
console.log(listItems[l]);
为了演示如何通过更改上下文来更改作用域,我们将使用上述代码中的 call () 方法。上面的代码使用 call () 方法重写,我们可以看到它如何改变代码的上下文及其作用域。
var listItems = document.querySelectorAll('ul li');
for (var l = 0; l < listItems.length; l++) {
    (function () {
                 console.log(this);
               }).call(listItems[l]);
}
以上输出如下所示。这次,正如您所看到的,<li> 元素被返回作为‘this’对象。通过这种方式,call() 函数在某些情况下非常强大。

公共和私有作用域
JavaScript 本身不像其他语言那样有 public 和 private 作用域,但我们可以使用模块模式来模拟它们。
让我们看下面的例子
var myModule = (function () {
            var _myPrivateMethod = function () {
                console.log('_myPrivateMethod()');
            }
            return {
                methodOne: function () {
                    console.log('methodOne()');
                }
            };
 })();
 myModule.methodOne();
 myModule._myPrivateMethod();   
在上面的代码中,行 myModule._myPrivateMethod();<font color="#111111" face="Segoe UI"> </font>会失败,因为函数 _myPrivateMethod() 是 private 方法,对 myModule 可访问。
然而,代码行 myModule.methodOne(); 会成功,因为该方法通过 return 语句被设为 public。
关注点
值得一读关于 bind() 和 apply() ,它们与 call() 类似,但略有不同。也值得详细阅读 module 模式,因为它是 Angular JS 的关键。


