【问题标题】:Getter/setter in constructor构造函数中的 getter/setter
【发布时间】:2011-07-10 11:34:34
【问题描述】:

我最近读到了一个事实,即有可能在 JavaScript 中定义 getter/setter。这似乎非常有用 - setter 是一种“帮助器”,它可以在实际设置之前先解析要​​设置的值。

例如,我目前有这样的代码:

var obj = function(value) {
    var test = !!value; // 'test' has to be a boolean
    return {
        get test() { return test },
        set test(value) { test = !!value }
    };
};

var instance = new obj(true);

此代码始终将value 转换为布尔值。所以如果你编码instance.test = 0,那么instance.test === false

但是,要使其正常工作,您必须实际返回一个 object,这意味着新实例不是 obj 类型,而只是一个普通对象。这意味着更改obj 的原型对实例没有影响。例如,这不起作用 - instance.func 未定义:

obj.prototype.func = function() { console.log(this.value); };

因为instance 不是obj 类型。为了让原型函数正常工作,我想我不应该返回一个普通对象,而是不返回任何东西,这样instance 将只是obj 类型,就像常规构造函数一样。

那么问题是如何实现getter/setter?我只能找到描述如何将这些添加到对象的文章,而不是作为自定义类型的构造函数的一部分。

那么如何在构造函数中实现getter/setter,既可以使用getter/setter,又可以扩展原型?

【问题讨论】:

  • @Pointy 小心那些东西。是的,那里速度较慢,但​​我怀疑它在几乎所有情况下都会导致明显的瓶颈。
  • 感谢那篇文章。不过,我没有遇到任何性能故障。
  • @Pointy 该链接现在已失效,您能提供替代方案吗?很好奇它意味着什么。
  • @ApathyBear 该死,为此使用链接缩短器是愚蠢的。我认为这与运行时性能有关。有人向 jsperf 提出了一个问题,为什么 getter/setter 函数最近这么慢(它们并没有那么慢,只是比你想象的要慢)。
  • @Pointy 这是来自互联网档案馆的死链接档案:web.archive.org/web/20110310163319/http://labs.transloc.com/…

标签: javascript prototype constructor getter-setter


【解决方案1】:

你不能那样做。

您可以为对象的属性设置 setter/getter。我建议你使用 ES5 Object.defineProperties。当然这只适用于现代浏览器。

var obj = function() {
    ...
    Object.defineProperties(this, {
        "test": {
             "get": function() { ... },
             "set": function() { ... }
        }
    });
}

obj.prototype.func = function() { ... }

var o = new obj;
o.test;
o.func();

【讨论】:

  • 这很好用。虽然打字要多一些,但仍然是一个明确的解决方案。
  • 这很棒。我希望语法更简洁一些,但它可以工作。我有点希望你能像使用普通函数一样做this.myProp = get () { }
  • 这不会在你每次做新的 obj 时创建 get/set 函数吗?
【解决方案2】:

我知道这可能为时已晚,但我想出了一种不同的方法来完成您想要的事情,并且为了像我这样的人,在这里搜索答案。

function Constructor(input){
     this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
    return this.input * 2;
});

var test = new Constructor(5);
alert(test.value) // 10

我已经在 chrome、safari、mobile safari、firefox 中对此进行了测试,它们都可以正常工作(当然是最新版本)

【讨论】:

  • 我不确定这是否适用于跨浏览器,但即使它确实看起来很恶心。你有什么理由选择这条路而不是像@Raynos 那样使用defineProperties()
  • 公平警告,此语法为deprecated。对于现代浏览器,适当的语法是Object.defineProperty(someObject, 'somePropertyName', { get: function(){}, set: function(val){} })
【解决方案3】:

@Alex 我认为这是更多的选择和更多的力量,编程就是艺术,@Nat 与我们分享他的发现,为此我感谢他。也许有人想那样做。

我确定 setter 版本是相同的,只是将 g 更改为 s。

例如:

function Constructor(input){
     this.input = input;
}

Object.__defineGetter__.call(Constructor.prototype, "value", function(){
    return this.input * 2;
});

Object.__defineSetter__.call(Constructor.prototype, "bar", function(foo){
    return this.input *= foo;
});

var test = new Constructor(5);
console.log(test.value); // 10
test.bar = 5;
console.log(test.input); //25

话虽如此,此功能已被弃用,建议不要在生产编码中使用。

【讨论】:

    【解决方案4】:

    ES6 更新——查看 Alex Rauschmayer 的书Exploring ES6http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data 的第 19.3.1 节,它演示了如何将 WeakMaps 与 getter 和 setter 一起使用来保存私有数据。结合第 16.2.2.3 节 http://exploringjs.com/es6/ch_classes.html#leanpub-auto-getters-and-setters 会产生类似

    # module test_WeakMap_getter.js
    var _MyClassProp = new WeakMap();
    class MyClass {
        get prop() {
            return _MyClassProp.get( this ); 
        }
        set prop(value) {
            _MyClassProp.set( this, value );
        }
    }
    var mc = new MyClass();
    mc.prop = 5 ;
    console.log( 'My value is', mc.prop );
    
    $ node --use_strict test_WeakMap_getter.js 
    My value is 5
    

    【讨论】:

      【解决方案5】:
      function Obj(value){
          this.value = !!value;
      }
      
      Obj.prototype = {
          get test () {
              return this.value;``
          },
          set test (value) {
              this.value = !!this.value;
          }
      };
      var obj = new Obj(true);
      

      【讨论】:

      • 请添加您的代码正在做什么的描述,以帮助 OP 更好地理解它。
      • 欢迎来到 SO!发布答案时,重要的是要解释您的代码并解释如何使用它
      • 有趣的是,我滚动了所有热门答案,因为在这种情况下解释代码的文本完全是浪费时间,你的代码解释了自己,这正是 OP 可能希望他使用的。谢谢。
      • 默认情况下替换原型不是一个好习惯,obj.prototype 将具有一个指向 Obj 的属性构造函数。如果您使用 instanceOf 来检查使用 Obj 创建的对象是否是它的实例,则需要添加它。更重要的是,这将是非常明确的,应该有更好的方法来做到这一点。
      【解决方案6】:

      通常你需要 class 方法。 @Raynos 在 2011 年 5 月 7 日的回答完成了工作,但它定义了 instance 方法,而不是类方法。

      下面说明了一个类定义,其中 getter 和 setter 是类的一部分。这个定义很像@Raynos 的答案,但在代码中有两个不同之处:(1)“defineProperties()”动作已从构造函数中移出。 (2) “defineProperties()”的参数由实例对象“this”改为构造函数的原型对象。

      function TheConstructor(side) {
        this.side = side;
      }
      
      Object.defineProperties(TheConstructor.prototype, {
              area: {
                   get: function()    { return this.side * this.side; }
                  ,set: function(val) { this.side = Math.sqrt(val);   }
              }
      });
      
      // Test code:
      
      var anInstance = new TheConstructor(2);
      console.log("initial  Area:"+anInstance.area);
      anInstance.area = 9;
      console.log("modified Area:"+anInstance.area);
      

      这会产生这些结果:

      initial  Area:4
      modified Area:9
      

      虽然通常是类与实例之间的区别 定义只是风格问题,有目的 良好的风格,并且在某些情况下区分很重要: memoized getter。 memoized getter 的目的是 此处描述:Smart/self-overwriting/lazy getters

      在类级别定义getter,当memoized值是 与整个班级有关。例如,一个配置文件 应该只读一次;然后应应用结果值 在节目期间。以下示例代码 在类级别定义一个 memoized getter。

      function configureMe() {
        return 42;
      }
      
      Object.defineProperties(TheConstructor.prototype, {
          memoizedConfigParam: {
              get: function() {
                  delete TheConstructor.prototype.memoizedConfigParam;
                  return TheConstructor.prototype.memoizedConfigParam = configureMe();
              }
              ,configurable:  true
          }
      });
      
      // Test code:
      
      console.log("memoizedConfigParam:"+anInstance.memoizedConfigParam);
      

      生产:

      memoizedConfigParam:42
      

      从示例中可以看出,memoized getter 具有 getter 函数删除自身的特征, 然后用一个简单的值替换自己 (大概)永远不会改变。 请注意,“configurable”必须设置为“true”。

      当memoized值时,在实例级别定义getter 取决于实例的内容。定义移动 在构造函数内部,关注的对象是'this'。

      function TheConstructorI(side) {
      
        this.side = side;
      
        Object.defineProperties(this, {
          memoizedCalculation: {
              get: function() {
                  delete this.memoizedCalculation;
                  return this.memoizedCalculation = this.expensiveOperation();
              }
              ,configurable:  true
          }
        });
      }
      
      TheConstructorI.prototype.expensiveOperation = function() {
        return this.side * this.side * this.side;
      }
      
      //Test code:
      
      var instance2 = new TheConstructorI(2);
      var instance3 = new TheConstructorI(3);
      
      console.log("memoizedCalculation 2:"+instance2.memoizedCalculation);
      console.log("memoizedCalculation 3:"+instance3.memoizedCalculation);
      

      生产:

      memoizedCalculation 2:8
      memoizedCalculation 3:27
      

      如果你想保证(而不是假设)记忆 值永远不会改变,“可写”属性需要 被改变。这使得代码有点复杂。

      function TheConstructorJ(side) {
      
        this.side = side;
      
        Object.defineProperties(this, {
          memoizedCalculation: {
              get: function() {
                  delete this.memoizedCalculation;
                  Object.defineProperty( this, 'memoizedCalculation'
                    ,{  value    : this.expensiveOperation()
                       ,writable : false
                    });
                  return this.memoizedCalculation;
              }
              ,configurable:  true
          }
        });
      }
      
      TheConstructorJ.prototype.expensiveOperation = function() {
        return this.side * this.side * this.side;
      }
      
      //Test code:
      
      var instanceJ = new TheConstructorJ(2);
      
      console.log("memoizedCalculation:"+instanceJ.memoizedCalculation);
      instanceJ.memoizedCalculation = 42;  // results in error
      

      生产:

      memoizedCalculation:8
      >Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
      

      从 2011 年 3 月 7 日起,OP 的原始问题提出了基本的 getter 和 setter 语法,注意到它在一个对象上工作,但是 不在'this'上,并询问如何在其中定义getter和setter 一个构造函数。除了上面所有的例子,还有 也是一种“廉价”的做法:在其中创建一个新对象 构造函数,就像 OP 所做的那样,但随后将对象分配给 成为“this”中的成员。所以,原始代码看起来像 这个:

      var MyClass = function(value) {
          var test = !!value; // 'test' has to be a boolean
          this.data = {
              get test() { return test },
              set test(value) { test = !!value }
          };
      };
      
      var instance = new MyClass(true);
      
      // But now 'data' is part of the access path
      instance.data.test = 0;
      console.log(instance.data.test);
      

      生产:

      false
      

      信不信由你,我实际上遇到过这样的情况 这种“廉价”是最好的解决方案。具体来说,我用这个 当我将几个表中的记录封装在其中时的技术 一个单一的类,并希望呈现一个统一的视图,好像 它们是一条称为“数据”的记录。

      玩得开心。

      IAM_AL_X

      【讨论】:

        猜你喜欢
        • 2013-07-30
        • 1970-01-01
        • 2017-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多