141

I've been trying to get my head around getters and setters and its not sinking in. I've read JavaScript Getters and Setters and Defining Getters and Setters and just not getting it.

Can someone clearly state:

  1. What a getter and setter are meant to do, and
  2. Give some VERY simple examples?
4

14 回答 14

104

除了@millimoose 的回答之外,setter 还可以用于更新其他值。

function Name(first, last) {
    this.first = first;
    this.last = last;
}

Name.prototype = {
    get fullName() {
        return this.first + " " + this.last;
    },

    set fullName(name) {
        var names = name.split(" ");
        this.first = names[0];
        this.last = names[1];
    }
};

现在,您可以设置fullNamefirstlast将被更新,反之亦然。

n = new Name('Claude', 'Monet')
n.first # "Claude"
n.last # "Monet"
n.fullName # "Claude Monet"
n.fullName = "Gustav Klimt"
n.first # "Gustav"
n.last # "Klimt"
于 2009-05-01T21:15:22.433 回答
63

JavaScript 中的 Getter 和 Setter

概述

JavaScript 中的 getter 和 setter 用于定义计算属性访问器。计算属性是使用函数获取或设置对象值的属性。基本理论是这样的:

var user = { /* ... object with getters and setters ... */ };
user.phone = '+1 (123) 456-7890'; // updates a database
console.log( user.areaCode ); // displays '123'
console.log( user.area ); // displays 'Anytown, USA'

这对于在访问属性时自动执行幕后操作非常有用,例如保持数字在范围内、重新格式化字符串、触发值已更改事件、更新关系数据、提供对私有属性的访问等等。

下面的示例显示了基本语法,尽管它们只是获取和设置内部对象值而不做任何特殊操作。在实际情况下,您将修改输入和/或输出值以满足您的需要,如上所述。

获取/设置关键字

ECMAScript 5 支持定义计算属性getset关键字。它们适用于除 IE 8 及以下版本之外的所有现代浏览器。

var foo = {
    bar : 123,
    get bar(){ return bar; },
    set bar( value ){ this.bar = value; }
};
foo.bar = 456;
var gaz = foo.bar;

自定义 Getter 和 Setter

get并且set不是保留字,因此可以重载它们以创建您自己的自定义跨浏览器计算属性函数。这适用于任何浏览器。

var foo = {
    _bar : 123,
    get : function( name ){ return this[ '_' + name ]; },
    set : function( name, value ){ this[ '_' + name ] = value; }
};
foo.set( 'bar', 456 );
var gaz = foo.get( 'bar' );

或者对于更紧凑的方法,可以使用单个函数。

var foo = {
    _bar : 123,
    value : function( name /*, value */ ){
        if( arguments.length < 2 ){ return this[ '_' + name ]; }
        this[ '_' + name ] = value;
    }
};
foo.value( 'bar', 456 );
var gaz = foo.value( 'bar' );

避免做这样的事情,这会导致代码膨胀。

var foo = {
    _a : 123, _b : 456, _c : 789,
    getA : function(){ return this._a; },
    getB : ..., getC : ..., setA : ..., setB : ..., setC : ...
};

对于上面的示例,内部属性名称使用下划线进行抽象,以阻止用户简单地做foo.barvsfoo.get( 'bar' )并获得“未煮熟”的值。name您可以根据要访问的属性的名称(通过参数)使用条件代码来执行不同的操作。

Object.defineProperty()

UsingObject.defineProperty()是另一种添加 getter 和 setter 的方法,可以在对象被定义后使用。它还可以用于设置可配置和可枚举的行为。此语法也适用于 IE 8,但不幸的是仅适用于 DOM 对象。

var foo = { _bar : 123 };
Object.defineProperty( foo, 'bar', {
    get : function(){ return this._bar; },
    set : function( value ){ this._bar = value; }
} );
foo.bar = 456;
var gaz = foo.bar;

__defineGetter__()

最后,__defineGetter__()是另一种选择。它已被弃用,但仍在网络上广泛使用,因此不太可能很快消失。它适用于除 IE 10 及以下版本之外的所有浏览器。尽管其他选项在非 IE 上也能很好地工作,但这个选项并不是那么有用。

var foo = { _bar : 123; }
foo.__defineGetter__( 'bar', function(){ return this._bar; } );
foo.__defineSetter__( 'bar', function( value ){ this._bar = value; } );

另外值得注意的是,在后面的示例中,内部名称必须与访问器名称不同以避免递归(即,foo.bar调用foo.get(bar)调用foo.bar调用foo.get(bar)...)。

也可以看看

MDN get , set , Object.defineProperty() , __defineGetter__() , __defineSetter__()
MSDN IE8 Getter 支持

于 2016-01-18T01:31:09.457 回答
59

You'd use them for instance to implement computed properties.

For example:

function Circle(radius) {
    this.radius = radius;
}

Object.defineProperty(Circle.prototype, 'circumference', {
    get: function() { return 2*Math.PI*this.radius; }
});

Object.defineProperty(Circle.prototype, 'area', {
    get: function() { return Math.PI*this.radius*this.radius; }
});

c = new Circle(10);
console.log(c.area); // Should output 314.159
console.log(c.circumference); // Should output 62.832

(CodePen)

于 2009-05-01T19:58:07.393 回答
16

很抱歉重新提出一个老问题,但我想我可能会提供一些非常基本的示例和傻瓜式的解释。到目前为止发布的其他答案都没有像MDN 指南的第一个示例那样说明语法,这几乎是人们所能获得的基本知识。

吸气剂:

var settings = {
    firstname: 'John',
    lastname: 'Smith',
    get fullname() { return this.firstname + ' ' + this.lastname; }
};

console.log(settings.fullname);

...John Smith当然会记录。getter的行为类似于变量对象属性,但提供了函数的灵活性来动态计算其返回值。它基本上是一种创建调用时不需要 () 的函数的奇特方式。

二传手:

var address = {
    set raw(what) {
        var loc = what.split(/\s*;\s*/),
        area = loc[1].split(/,?\s+(\w{2})\s+(?=\d{5})/);

        this.street = loc[0];
        this.city = area[0];
        this.state = area[1];
        this.zip = area[2];
    }
};

address.raw = '123 Lexington Ave; New York NY  10001';
console.log(address.city);

...将登录New York到控制台。与 getter 一样,setter的调用语法与设置对象属性的值相同,但它是另一种不带 () 调用函数的奇特方式。

请参阅这个 jsfiddle以获得更彻底、可能更实际的示例。将值传递给对象的设置器会触发其他对象项的创建或填充。具体来说,在 jsfiddle 示例中,传递一个数字数组会提示 setter 计算均值、中位数、众数和范围;然后为每个结果设置对象属性。

于 2014-10-03T21:34:03.703 回答
11

只有当您拥有类的私有属性时,getter 和 setter 才真正有意义。由于 Javascript 并没有像您通常从面向对象语言中想到的那样真正具有私有类属性,因此可能很难理解。这是私有计数器对象的一个​​示例。这个对象的好处是不能从对象外部访问内部变量“count”。

var counter = function() {
    var count = 0;

    this.inc = function() {
        count++;
    };

    this.getCount = function() {
        return count;
    };
};

var i = new Counter();
i.inc();
i.inc();
// writes "2" to the document
document.write( i.getCount());

如果您仍然感到困惑,请查看 Crockford 的关于Private Members in Javascript的文章。

于 2009-05-01T20:53:34.827 回答
8

I think the first article you link to states it pretty clearly:

The obvious advantage to writing JavaScript in this manner is that you can use it obscure values that you don't want the user to directly access.

The goal here is to encapsulate and abstract away the fields by only allowing access to them thru a get() or set() method. This way, you can store the field/data internally in whichever way you want, but outside components are only away of your published interface. This allows you to make internal changes without changing external interfaces, to do some validation or error-checking within the set() method, etc.

于 2009-05-01T19:57:12.043 回答
6

虽然我们经常习惯于看到没有任何访问控制的具有公共属性的对象,但 JavaScript 允许我们准确地描述属性。事实上,我们可以使用描述符来控制如何访问属性以及我们可以对其应用哪些逻辑。考虑以下示例:

var employee = {
    first: "Boris",
    last: "Sergeev",
    get fullName() {
        return this.first + " " + this.last;
    },
    set fullName(value) {
        var parts = value.toString().split(" ");
        this.first = parts[0] || "";
        this.last = parts[1] || "";
    },
    email: "boris.sergeev@example.com"
};

最终结果:

console.log(employee.fullName); //Boris Sergeev
employee.fullName = "Alex Makarenko";

console.log(employee.first);//Alex
console.log(employee.last);//Makarenko
console.log(employee.fullName);//Alex Makarenko
于 2016-10-25T13:35:03.203 回答
3

您可以通过构造函数的原型为 js 类定义实例方法。

以下是示例代码:

// BaseClass

var BaseClass = function(name) {
    // instance property
    this.name = name;
};

// instance method
BaseClass.prototype.getName = function() {
    return this.name;
};
BaseClass.prototype.setName = function(name) {
    return this.name = name;
};


// test - start
function test() {
    var b1 = new BaseClass("b1");
    var b2 = new BaseClass("b2");
    console.log(b1.getName());
    console.log(b2.getName());

    b1.setName("b1_new");
    console.log(b1.getName());
    console.log(b2.getName());
}

test();
// test - end

而且,这应该适用于任何浏览器,您也可以简单地使用 nodejs 来运行此代码。

于 2014-06-16T03:27:58.817 回答
2

有什么令人困惑的... getter 是在您获取属性时调用的函数,setter 是在您设置它时调用的函数。例如,如果你这样做

obj.prop = "abc";

您正在设置属性 prop,如果您使用的是 getter/setter,则将调用 setter 函数,并将“abc”作为参数。对象内部的 setter 函数定义理想情况下应该是这样的:

set prop(var) {
   // do stuff with var...
}

我不确定跨浏览器实现的效果如何。似乎 Firefox 也有另一种语法,带有双下划线的特殊(“魔术”)方法。像往常一样,Internet Explorer 不支持任何这些。

于 2011-04-28T11:19:14.777 回答
2

如果您指的是访问器的概念,那么简单的目标是隐藏底层存储以防止任意操作。最极端的机制是

function Foo(someValue) {
    this.getValue = function() { return someValue; }
    return this;
}

var myFoo = new Foo(5);
/* We can read someValue through getValue(), but there is no mechanism
 * to modify it -- hurrah, we have achieved encapsulation!
 */
myFoo.getValue();

如果您指的是实际的 JS getter/setter 功能,例如。defineGetter/defineSetter{ get Foo() { /* code */ } },那么值得注意的是,在大多数现代引擎中,这些属性的后续使用将比其他情况慢得多。例如。比较性能

var a = { getValue: function(){ return 5; }; }
for (var i = 0; i < 100000; i++)
    a.getValue();

对比

var a = { get value(){ return 5; }; }
for (var i = 0; i < 100000; i++)
    a.value;
于 2009-05-01T20:51:32.523 回答
2

我也对我阅读的解释感到有些困惑,因为我试图将一个属性添加到我没有编写的现有原型中,因此替换原型似乎是错误的方法。因此,为了后代,这是我将last属性添加到的方式Array

Object.defineProperty(Array.prototype, "last", {
    get: function() { return this[this.length - 1] }
});

比添加功能恕我直言要好一些。

于 2014-09-16T16:53:31.673 回答
0

这里没有 ES6 的例子class(现在甚至不是“新的”,这是常态):

class Student {

    contructor(firstName, lastName){
        this.firstName = firstName
        this.lastName = lastName
        this.secretId = Math.random()    
    }
    
    get fullName() {
        return `${this.firstName} ${this.lastName}`; // this is backtick in js, u can check it out here: https://stackoverflow.com/a/27678299/12056841
    }

    set firstName(newFirstName) {
        // validate that newFirstName is a string (and maybe limit length)
        this.firstName = newFirstName
    }

    get studentId() { return this.secretId }
}

并且没有设置secretId 的方法,因为我们不希望任何人更改它。

** 如果secretId根本不应该更改,一个不错的方法是通过向其添加“#”将其声明为此类的“私有”(例如:this.#secretId = Math.random()return this.#secretId

更新:关于支持字段您可能需要重命名您的字段 - 或您的 setter 函数,但更改您的字段名称对我来说更有意义。一个选项就像我上面提到的(使用 a#将字段声明为“私有”)。另一种方法是重命名它 ( _firstName, firstName_...)

于 2021-08-02T05:54:38.880 回答
0

您还可以使用__defineGetter__

function Vector2(x,y) {
    this.x = x;
    this.y = y;
}

Vector2.prototype.__defineGetter__("magnitude", function () {
   return Math.sqrt(this.x*this.x+this.y*this.y);
});

console.log(new Vector2(1,1).magnitude)

或者,如果您愿意:

function Vector2(x,y) {
    this.x = x;
    this.y = y;
    this.__defineGetter__("magnitude", function () {
       return Math.sqrt(this.x*this.x+this.y*this.y);
    });
}



console.log(new Vector2(1,1).magnitude)

但是这个功能最近被标记为“旧版”,被Object.defineProperty().

于 2021-04-16T01:47:14.200 回答
-1

我为你们准备了一个可能有点难看的东西,但它确实可以跨平台完成

function myFunc () {

var _myAttribute = "default";

this.myAttribute = function() {
    if (arguments.length > 0) _myAttribute = arguments[0];
    return _myAttribute;
}
}

这样,当你打电话时

var test = new myFunc();
test.myAttribute(); //-> "default"
test.myAttribute("ok"); //-> "ok"
test.myAttribute(); //-> "ok"

如果你真的想增加趣味.. 你可以插入一个 typeof 检查:

if (arguments.length > 0 && typeof arguments[0] == "boolean") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "number") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "string") _myAttribute = arguments[0];

或者更疯狂地使用高级 typeof 检查: codingforums.com 上的type.of() 代码

于 2010-07-09T14:15:03.100 回答