代码之家  ›  专栏  ›  技术社区  ›  Goyuix

Javascript闭包和数据可见性

  •  12
  • Goyuix  · 技术社区  · 15 年前

    我试图围绕类、数据可见性和闭包(特别是在Javascript中)的概念进行思考,我在类型的jQuery文档页面上提到闭包用于隐藏数据:

    该模式允许您使用对外部不可见的数据进行操作的方法创建对象,这正是面向对象编程的基础。

    function create() {
      var counter = 0;
      return {
        increment: function() {
          counter++;
        },
        print: function() {
          console.log(counter);
        }
      }
    }
    var c = create();
    c.increment();
    c.print(); // 1
    

    通过使用关键字var声明变量计数器,它已经在函数/类定义中的局部范围内。据我所知和所知,从一开始就无法从外部访问它。从数据可见性的角度来看,我是否遗漏了一些内容。

    第二,像上面那样编写类与像下面这样编写类相比是否有优势:

    function create() {
      var counter = 0;
      this.increment = function() {
          counter++;
      }
      this.print = function() {
          console.log(counter);
      }
      return this;
    }
    var c = create();
    c.increment();
    c.print(); // 1
    

    据我所知,它们在语义上或多或少是相同的——第一种是更“jQuery风格”。我只是想知道第一个例子中是否有我没有完全理解的优势或其他细微差别。如果我是正确的,这两个示例都创建了闭包,因为它们访问的是在其自身范围之外声明的数据。

    http://docs.jquery.com/Types#Closures

    9 回复  |  直到 15 年前
        1
  •  10
  •   Kenan Banks    15 年前

    首先,两个版本都使用闭包是正确的。

    在我看来,第一个版本更干净,在现代javascript中更受欢迎。第一种样式的主要潜在缺点是无法有效地将对象分配给构造函数的原型,如果要创建许多相同的对象,这将非常有用(而且效率更高)。

    第二种风格,我实际上从未在生产Javascript中见过。通常,您会实例化 create new ,而不是返回 this create() 功能,就像这样:

    function create() {
      var counter = 0;
      this.increment = function() {
          counter++;
      }
      this.print = function() {
          console.log(counter);
      }
    }
    var c = new create();
    c.increment();
    c.print(); // 1
    
        2
  •  4
  •   Russ Cam    15 年前

    通过使用 关键字var,它已经在本地 作用域在函数/类内部 释义就我所知所能 告诉我,它无法从 首先在外面。我失踪了吗 来自数据可见性的一些东西 态度

    并不是说 counter 变量不能从函数外部访问,这是因为 increment print 之后的功能 create closures 太有用了。

        3
  •  2
  •   Miles    15 年前

    您应该将示例与此代码段进行比较

    function create() {
      this.counter = 0;
      this.increment = function() {
          this.counter++;
      };
      this.print = function() {
          console.log(counter);
      }
    }
    
    var c = new create();
    c.increment();
    c.print(); // 1
    

    因此,当调用new create()时,它使用两个方法和一个实例变量(即:counter)初始化新对象。Javascript本身没有封装,因此您可以访问c.counter,如下所示:

    var c = new create();
    c.increment();
    c.counter = 0;
    c.print(); // 0
    

    通过使用闭包(如示例所示),计数器现在不再是一个实例字段,而是一个局部变量。一方面,您不能从create()函数外部访问。另一方面,increment()和print()可以访问,因为它们在封闭范围内关闭。因此,我们最终得到了一个非常好的对象封装仿真。

        4
  •  2
  •   Jason Bunting    15 年前

    好吧,我不想卷入一场关于如何在JavaScript中创建对象的宗教战争,因为有些人强烈认为有正确和错误的方法。

    但是,我想指出第二组代码中的一些不太有趣的东西,即,您正在为包含在 this 关键词-你知道那个物体是什么吗?除非使用如下实例化语法,否则它不是空对象:

    var c = new create();
    

    当你这么做的时候

    this = {};
    

    但是当你打电话的时候 create()

        5
  •  1
  •   Joel Coehoorn    15 年前

    您的第二个示例仍然使用闭包,因为increment和print函数仍然作用于变量,否则在调用时超出范围 c.increment()

    this 关键字,并使用javascript “这可能很棘手——它并不总是指它看起来应该指的东西。

        6
  •  1
  •   Steerpike    15 年前

    克里斯蒂安·海尔曼在这方面写了一篇相当不错的文章 the module pattern 你所描述的可能有助于你理解它,以及它为什么有用。

        7
  •  1
  •   Matt    15 年前

    从OOP背景来看,这种语法更有意义:

    Create = function {
      // Constructor info.
    
      // Instance variables
      this.count = 10;
    }
    
    Create.prototype = {
    
         // Class Methods
         sayHello : function() {
              return "Hello!";
         },
    
         incrementAndPrint : function() {
              this.count++;
    
              // Inner method call.
              this.print();
         },
    
         print : function() {
             return this.count;
         }
    
    
    }
    
    var c = new Create();
    alert(c.incrementAndPrint());
    
        8
  •  1
  •   sth    14 年前
    MYAPP = (function(){
       var v1,v2;
       return {
        method1:function(){},
        method2:function(){}
       };
    })();
    

    我总是在我的应用程序中使用这样的闭包,我自己定义的所有方法都在MYAPP命名空间中,v1和v2只能由MYAPP中的方法访问。在我的应用程序中,我通常只编写一个“app.js”文件,所有的js代码都在里面。我想您可以定义一个名为“Registry”的方法来定义MYAPP中的私有变量,然后在方法中使用它。当您想在html文件中添加额外的代码时,所有额外的变量和方法都应该由Registry方法定义,就像JQuery.extend方法一样。我听说如果在IE浏览器中使用太多闭包,很容易导致堆栈溢出。(在我看来)

        9
  •  0
  •   Miles    15 年前

    create() ,在职能范围内, this 是全局对象(在调用“bare”函数时总是如此,而不将其用作构造函数或将其作为属性访问(例如“method”调用))。在浏览器中,全局对象是 window . 因此,当您调用create CONTAILED times时,它会创建新的闭包,但随后您会将它们指定给与以前相同的全局对象,覆盖旧函数,这不是您想要的:

    var c = create(); // c === window
    c.increment();
    c.print(); // 1
    var c2 = create(); // c2 === c === window
    c.print(); // 0
    c2.print(); // 0
    increment(); // called on the global object
    c.print(); // 1 
    c2.print(); // 1
    

    正如其他人指出的那样,解决办法是使用 new create() .