【问题标题】:Trying to work through this closure example...?试图完成这个闭包示例......?
【发布时间】:2014-01-16 11:00:36
【问题描述】:
var o = Object.create({inherited: 1}, {
foo: {
  get: (function () { // a closure
      var closured = 'foo';
      return function () {
        return closured+'bar';
      };
    })()
  }
});

o.foo; // "foobar"

我遇到了一些问题。我真的不明白我们是如何得到字符串"foobar" 的。我会写下我目前对这段代码的错误理解,希望有人能解释我错在哪里。

  1. o 是一个具有foo 属性的对象。
  2. foo 对应于具有一个属性 get 的对象。 (这似乎是错误的,但我看不出如何。)
  3. get 对应于自调用匿名函数,它返回由一行 return closured+'bar'; 组成的函数

话虽如此,我希望o.foo 返回一个对象,o.foo.get 返回一个函数,o.foo.get() 返回"foobar"

但这不是正在发生的事情,我不知道为什么。

另外,另外,你为什么要这样写东西?为什么写:

get: (function () { // a closure
      var closured = 'foo';
      return function () {
        return closured+'bar';
      };
    })()

而不是简单地:

get: (function () { // a closure
      var closured = 'foo';
        return closured+'bar';
    })()

额外的功能层有什么好处?

【问题讨论】:

    标签: javascript function closures


    【解决方案1】:

    Object.create 的第二个参数应该是一个属性描述符对象。这是一个描述要添加到原型的属性的对象,而不是对象本身。例如:

    var o = Object.create({inherited: 1}, {
       foo: { /* ... property descriptor object ... */ },
       bar: { /* ... property descriptor object ... */ }
    });
    

    在上面的示例中,我们向新对象的原型添加了两个属性:foobar。这些属性中的每一个都可以使用描述符对象以各种方式进行配置。

    描述符对象接受多个配置属性,即:configurableenumerablevaluewritablegetset

    在您的原始示例中,您有效地为属性foo 配置了一个getter 函数。如果您希望简单地设置属性foo 的值,那么您必须在描述符内创建一个value 属性并在那里添加您想要的值(如果您选择,它实际上是一个具有名为get 的属性的对象)。例如:

    var o = Object.create({inherited: 1}, {
       foo: {
         value: {get: (function () { /* ... code ... */ })() }
       }
    });
    

    有关属性描述符对象的更多详细信息,请参阅:Object.defineProperty()

    【讨论】:

      【解决方案2】:

      关于代码,有几处你不明白。

      var o = Object.create({inherited: 1}, {
        foo: {
          get: function(){ return 1; }
        }
      });
      
      console.log(o.foo); // 1
      o.foo = 5;
      console.log(o.foo); // 1
      

      Object.create 的第二个参数描述了对象的属性。在这种情况下,get 部分实际上是一个 getter,因此它描述了在读取对象 ofoo 属性时会发生什么。


      (function () { 
        var closure = 'foo';
        return function () {
          return 'bar';
        };
      })()
      

      这种结构称为 IIFE。这是一个立即调用的函数。 IIFE 的优点是它创建了一个局部作用域,因此closure 变量在外部作用域中不可见。这样可以避免冲突。


      get: (function () { // a closure
        var closured = 'foo';
        return function () {
          return closured+'bar';
        };
      })()
      // versus:
      
      get: function () { // changed it a bit to be a getter
        var closured = 'foo';
        return closured+'bar';
      }
      

      在第一种情况下,闭包变量在 getter 函数之外,而在第二种情况下,它在里面。在外部意味着它的状态不会在每次调用函数(getter)时都被重置,并且它也可以在其他函数中使用,它们都将共享相同的变量。这是一个潜在的用例:

      var namespace = Object.create(null, {
        uniqueId : {
          get : (function(){
            var counter = 0;
            return function() {
              return counter++;
            };
          })()
        }
      });
      
      console.log(namespace.uniqueId); // 0
      console.log(namespace.uniqueId); // 1
      

      【讨论】:

        【解决方案3】:

        foo对应一个有一个属性的对象,而那个属性就是你说的一个函数,但是Object.create的第二个参数是用来定义属性的。 o.foo 是新对象 o 的此类属性之一。它定义了一个 getter,因此它不是“值属性”而是“访问器属性”。尝试从 o.foo 读取会导致调用 getter。

        至于

        get: (function () { // a closure
              var closured = 'foo';
              return function () {
                return closured+'bar';
              };
        })()
        

        它返回一个函数,而

        get: (function () { // a closure
              var closured = 'foo';
                return closured+'bar';
        })()
        

        返回字符串'foobar',所以它们不等价,但第一个代码块等价于

        get: function () { // a closure
              var closured = 'foo';
                return closured+'bar';
        }
        

        它也返回一个函数。在示例中,该闭包是无用的,但您可以这样做

        foo: (function() {
            var closured = 'foo';
            return {
                'get': function () {
                    return closured+'bar';
                },
                'set': function (value) {
                    closured = value;
                }
            };
        })()
        

        这样,getter 和 setter 都可以访问closured,但没有其他函数可以:

        console.log(o.foo); // "foobar"
        o.foo = "a";
        console.log(o.foo); // "abar"
        

        【讨论】:

          【解决方案4】:

          您的两个问题的答案都可以在this documentation page 上找到。如果您阅读有关属性的部分,您会发现 Object.create 函数的第二个参数不是简单地合并到原型中,而是实际上是使用 getter、setter 和各种选项定义属性。

          考虑到这一点,看看下面的代码:

          get: (function () { // a closure
            var closured = 'foo';
            return function () {
              return closured+'bar';
            };
          })()
          

          正如您所说,分配了一个自调用匿名函数来获取。该函数返回一个关闭闭包变量并返回闭包+bar 值的函数。这不等同于以下内容:

          get: (function () { // a closure
            var closured = 'foo';
              return closured+'bar';
          })()
          

          原因是在这种情况下 get 被分配了一个实际值而不是一个函数,这破坏了 Object.create 期望的属性规范。

          【讨论】:

            【解决方案5】:

            使用get,您可以为名称为foo 的属性定义getter 方法。所以每次你对foo有读权限时,它都会执行getter方法。这就是为什么立即执行的函数本身会返回一个函数(它是生成的 getter)。

            执行o.foo 将导致对ofoo 属性的读取访问,因此将执行getter。

            【讨论】:

              【解决方案6】:

              闭包是在函数之外访问变量的能力。我想你已经知道了。

              那么现在来回答为什么要这样做的问题

              get: (function () { // a closure
                var closured = 'foo';
                return function () {
                  return closured+'bar';
                };
              })()
              

              你看,当你这样做时,你不仅可以得到“闭包”变量,还可以修改它!(虽然你做了修改)而且'闭包'现在不会被回收。所以它会留在内存中,而不是在函数返回时清理。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-08-21
                • 1970-01-01
                • 2010-10-15
                • 2021-05-07
                • 1970-01-01
                相关资源
                最近更新 更多