65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (4投票s)

2016年1月5日

CPOL

6分钟阅读

viewsIcon

11764

downloadIcon

78

关于 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() 内部访问,因为外部局部作用域可以在内部局部作用域中访问。

函数作用域

作用域不是为每个语句创建的,而是为每个函数创建的。当使用 FORWHILE 语句时,不会创建新的作用域。函数即新作用域,这是基本规则。

示例

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 本身不像其他语言那样有 publicprivate 作用域,但我们可以使用模块模式来模拟它们。

让我们看下面的例子

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 的关键。

© . All rights reserved.