【问题标题】:Best way to create class getter/setters in Javascript?在 Javascript 中创建类 getter/setter 的最佳方法?
【发布时间】:2011-05-25 22:56:47
【问题描述】:

来自 C#/PHP,我希望在使用 Javascript 创建的类(函数)上有完整的 getter/setter。

然而,在我遇到的大部分 Javascript 代码中,并没有使用 getter 和 setter,而是使用简单的公共变量。

我很高兴在 getter 和 setter 上找到 John Resig's article,但是上面的一些 cmets 声明某些浏览器“不支持 getter 和 setter”,这让我感到困惑,因为它们不是 Javascript 的“功能”,而是更多的是使用基本 Javascript 语法的简单模式。这篇文章也是 2007 年写的,所以现在可能已经过时了。

Javascript 中的 getter 和 setter 的当前状态是什么?它们是否确实被当今所有浏览器“支持”(无论这意味着什么)?它们是 Javascript 的有用编程模式,还是 Javascript 类(作为函数)更好地使用公共变量?有没有比以下更好的方法来实现它们?

$(document).ready(function() {
    var module = new Module('idcode');
    module.set_id_code('new idcode');
    module.set_title('new title');
    $('body').html(module.get_id_code()+': '+module.get_title());
});

function Module(id_code, title) {
    var id_code = id_code;
    var title = title;

    //id_code
    this.get_id_code = function() {
        return id_code;
    }
    this.set_id_code = function(value) {
        id_code = value;
    }

    //title
    this.get_title = function() {
        return title;
    }
    this.set_title = function(value) {
        title = value;
    }
}

【问题讨论】:

  • 确切地说,我不希望代码膨胀,但我希望像在 PHP 和 C# 等其他语言中那样对我的类进行公共和私有访问。 PHP 有 __get 和 __set 魔术方法,而 C# 有它的“属性语法”来获得分离而没有臃肿的语法。 Javascript中有这样的功能吗?
  • 上述代码在 IE 7/8/9 中是否有效??

标签: javascript oop getter-setter


【解决方案1】:

Firefox、Safari、Chrome 和 Opera(但不是 IE)都内置了相同的非标准 gettersetter 机制。ECMAScript 5 包含不同的语法 currently making its way into browsers,并将成为未来。 IE 8 已经具有此功能,但仅限于 DOM 节点,而不是常规的原生 JavaScript 对象。语法如下:

var obj = {};

Object.defineProperty(obj, "value", {
    get: function () {
        return this.val;
    },
    set: function(val) {
        this.val = val;
    }
});

【讨论】:

    【解决方案2】:

    我认为你没有抓住重点。 (或者也许其他回答者没有抓住重点。)ECMAScript 提供了一个“幕后”的 getter/setter 机制,因此

    x.foo = 3;
    y = x.foo;
    

    真的翻译成(某种)

    x.PutValue("foo",3);
    y = x.GetValue("foo");
    

    其中PutValueGetValue 是未命名的,不能直接访问属性的setter 和getter 函数。 (参见ECMAScript standard, 3rd ed.,第 8.7.1 和 8.7.2 节)第 3 版似乎没有明确定义用户如何设置自定义 getter 和 setter 函数。 Mozilla 对 Javascript 的实现曾经并且现在仍然如此,例如(这是在使用 Javascript 1.8 的 JSDB 中):

    js>x = {counter: 0};
    [object Object]
    js>x.__defineGetter__("foo", function() {return this.counter++; });
    js>x.foo
    0
    js>x.foo
    1
    js>x.foo
    2
    js>x.foo
    3
    js>x.foo
    4
    

    语法是(或至少到目前为止)特定于浏览器的。尤其是缺少 Internet Explorer,至少在 this SO question 看来是这样。

    ECMAScript 标准的第 5 版似乎确实对这种机制进行了标准化。见this SO question on getters and setters


    edit:一个更实际的例子,也许是为了你的目的:

    function makePrivateObject()
    {
       var state = 0;
       var out = {};
       out.__defineSetter__("foo", function(x) {});
       // prevent foo from being assigned directly
       out.__defineGetter__("foo", function() { return state; });
       out.count = function() { return state++; }
       return out;
    }
    
    js>x = makePrivateObject()
    [object Object]
    js>x.foo
    0
    js>x.foo = 33
    33
    js>x.foo
    0
    js>x.count()
    0
    js>x.count()
    1
    js>x.count()
    2
    js>x.foo
    3
    

    【讨论】:

    • 这可能是它们在内部的处理方式,但从技术上讲,如果我无法控制对它们的访问,它们仍然是“公共变量”。对我来说,getter 和 setter 的优点是我可以确定例如“Salary”属性只能在内部更改,或者例如当它被改变时,(a) 改变被记录,(b) 并且事件被触发,等等。除非我有 getter/setter 访问器方法,否则我没有能力为我的类定义这一层功能。跨度>
    • 啊——但这里有一个微妙之处;如果你有能力使用自定义的 getter/setter 方法,你就可以。我将编辑我的答案。
    【解决方案3】:

    怎么样:

    function Module(id_code, title) {
        var id_code = id_code;
        var title = title;
    
        var privateProps = {};
    
        this.setProperty = function(name, value) {
          // here you could e.g. log something
          privateProps[name] = value;
        }
    
        this.getProperty = function(name) {
          return privateProps[name];
        }
    }
    

    这里的 getter 和 setter 方法作用于一个私有对象,用于存储无法从任何其他方法访问的属性。因此,例如,您可以实现一个 setter(或 getter),在修改属性时记录或执行 ajax 或其他任何操作(getter/setter 方法的目的之一)。

    【讨论】:

    • 那不提供任何有用的功能。 setProperty 和 getProperty 是公共函数,所以这只是增加了一层间接性。它没有添加调用私有 getter 和 setter 函数的能力。
    • 它添加了只能通过 getter 和 setter 方法访问的属性 - 就像公共 getter 和 setter 方法用于修改其他语言中的私有成员一样。这是一个演示 - 这些方法也可以做其他事情。
    • 很公平——但是要么在你​​的代码中添加一个钩子来做到这一点,要么在你的代码中添加一个可以修改行为的注释。是的,这对于资深的 javascript 程序员来说是显而易见的,但对于初学者来说却不是。
    • @Jason S - 添加了一些解释。
    【解决方案4】:

    您实际上可以在 javascript 中定义 setter 和 getter,而不仅仅是模仿它们。我认为它适用于除 IE8 及以下的所有浏览器。

    $(document).ready(function() {
      var module = new Module('idcode');
      module.id = 'new idcode';
      module.title = 'new title';
      $('body').html(module.id + ': ' + module.title);
    });
    
    function Module(id, title) {
      return {
        id_code: id_code,
        _title: title,
    
        get id() {
          return id_code;
        },
        set id(value) {
          id_code = value;
        },
    
        get title() {
          return _title;
        },
        set title(value) {
          title = value;
        }
      }
    };
    

    【讨论】:

      【解决方案5】:

      我喜欢这个:

      //  Module
      var Module = function() {
      
          // prive getter/setter value
          var __value;
      
          // private getter/setter method
          var _value = function() {
              if(!arguments.length) return __value;
              else __value = arguments[0];
          };
      
          // output/public     
          return {
              value: _value
          };
      }
      

      【讨论】:

        【解决方案6】:

        Getter 和 setter 不是功能,而是不支持属性语法的语言的“设计模式”(在这种情况下为代码膨胀)。

        由于 Javascript 不需要 getter 和 setter,因此您不想编写它们。使用您可以使用的语言功能,在一种语言中效果很好的习语在另一种语言中效果不佳。

        我最喜欢的一句话来自 Python 社区:

        我们都是同意这里的成年人

        在讨论为什么不需要私有变量和信息隐藏时。

        可以在here找到报价。

        了解该语言为您提供了什么,并接受它的文化和规则。

        【讨论】:

        • -1 属性修改器对于正确封装是必不可少的。允许对象进入另一个对象的内部会导致可怕的错误!它将无法保持其内部状态一致。此外,该语言本身也支持 getter 和 setter。
        【解决方案7】:

        我喜欢的一种可靠的语义方式是使用settersgetters。例如,我创建了以下对象:

        var myComplexObject = {
          changelog: {},
          log: function(name, val) {
            console.log("set " + name + " to " + val);
            if (!this.changelog[name])
              this.changelog[name] = [];
            this.changelog[name].push(val);
          },
          set o (val) {
            this.log("o", val);
          },
          get o () {
            console.log("You will never know the true value!");
            return 42;
          }
        }
        

        在这里,每当您读取或修改o 的值时,行为都是自定义的。以以下代码行及其控制台输出为例:

        myComplexObject.o = "oxygen";
        
        set o to oxygen
        

        然后,在此示例中,尝试读取该值会导致:

        var read = myComplexObject.o;
        console.log(read);
        
        You will never know the true value!
        42
        

        而且,在本例中,您设置的每个新值都会被记录:

        myComplexObject.o = "Oh no!";
        console.log(myComplexObject.changelog.o);
        
        set o to Oh no!
        ["oxygen", "Oh no!"]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-11-03
          • 2017-02-17
          • 2014-11-18
          • 2013-07-09
          • 1970-01-01
          • 2012-12-03
          • 2012-12-12
          • 1970-01-01
          相关资源
          最近更新 更多