JavaScript中创建类的getter/setter的最佳方法是什么?

17

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

然而,在我遇到的许多Javascript代码中,并未使用getter和setter,而是简单的公共变量。

我很高兴发现John Resig的文章关于getter和setter,但其中一些评论指出一些浏览器“不支持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中有类似的功能吗? - Edward Tanguay
以上的代码在IE 7/8/9中是否有效? - Shaggy
7个回答

15
Firefox、Safari、Chrome和Opera(但不包括IE)都内置了相同的非标准gettersetter机制。ECMAScript 5包含了一种不同的语法,目前正在逐渐进入浏览器,并将成为未来的标准。IE 8已经具备此功能,但仅适用于DOM节点,而不是常规本地JavaScript对象。这就是语法的样子:
var obj = {};

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

9

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

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

真正的翻译(有点像)

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

其中PutValueGetValue是未命名的、不直接可访问的函数,用于设置和获取属性的setter和getter。 (详见ECMAScript标准第3版,第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中,缺乏支持,至少根据 这个SO问题

ECMAScript标准的第5版似乎规范化了这种机制。请参见有关 getter 和 setter 的 此 SO 问题


编辑:也许,一个更实际的例子,适用于您的目的:

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

2
这可能是它们在内部处理的方式,但从技术上讲,如果我无法控制对它们的访问,则它们仍然是“公共变量”。对于我来说,使用getter和setter的优点在于我可以确定例如“薪水”属性只能在内部更改,或者例如当它被更改时,(a)更改已记录,(b)并触发事件等。除非我有getter/setter访问器方法,否则我无法为我的类定义此层功能。 - Edward Tanguay
2
啊,但这里有一个微妙之处;如果您有使用自定义getter/setter方法的能力,那么您可以。我会编辑我的答案。 - Jason S

5

怎么样:

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方法的目的之一)。

2
这并没有提供任何有用的功能。 setProperty 和 getProperty 是公共函数,因此这只是增加了一个间接层。它并不能调用私有的 getter 和 setter 函数。 - Jason S
它添加了一些属性,这些属性除了通过getter和setter方法之外无法访问-就像其他语言中用于修改私有成员的公共getter和setter方法一样。这是一个演示 - 这些方法也可以执行其他操作。 - sje397
很好,但是请在您的代码中添加一个钩子来实现这一点,或者在代码中添加一个注释以便修改行为。对于经验丰富的JavaScript程序员来说,这显然是很明显的,但对于初学者来说则不然。 - Jason S
@Jason S - 添加了一些解释。 - sje397

5

实际上,你可以在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;
    }
  }
};

2

我喜欢这个:

//  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
    };
}

0

我喜欢使用可靠的、语义化的方式,即使用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!"]

0

Getters和Setters不是特性,而是针对不支持属性语法的语言所使用的"设计模式"(在这种情况下会造成代码冗余)。

由于Javascript不需要Getters和Setters,因此您不需要编写它们。使用可用于您的语言功能和惯用语,一个语言中适用的做法并不一定适用于另一个。

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

我们都是自愿参与者

当讨论私有变量和信息隐藏是否必要时。

该引用可以在这里找到。

学习语言的提供的功能并接受其文化和规则。


2
-1 属性修改器对于正确的封装非常重要。允许对象进入另一个对象的内部会导致可怕的错误!它将无法保持其内部状态的一致性。此外,该语言本身支持获取器和设置器。 - Nate Zaugg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接