我一直在努力弄清楚getter和setter,但它并没有陷入。我已经读过JavaScript Getters和Setters以及定义Getters和Setters,只是没有得到它。

有人可以清楚地说明吗:


getter和setter的用途是什么,并且
给出一些非常简单的示例?


评论

就个人而言,我不知道您能得到比约翰的更清晰的解释。

在OO世界中,getter和setter帮助编写正确封装的类

基本上,我是这样看的:您正在定义用于获取和设置属性的重载,这些重载是函数;但是,您不必致电给他们。这样,您可以替换= setValue(5);。 a = 5;和setValue()将因此在后台被调用以执行您喜欢的任何事情。

#1 楼

除了@millimoose的答案,setter也可以用于更新其他值。

现在,您可以设置fullName,并且firstlast将被更新,反之亦然。反之亦然。

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


评论


@Akash:否,不过,Internet Explorer 9确实支持可以定义getter和setter的较新的Object.defineProperty函数。

–马修·克鲁姆利
2011-3-22 15:46

MS不能正确地支持JS并不会使他们的Silverlight到处运行真的不是很痛苦,所以我必须对所有程序进行两次编程,一次用于SL,另一次用于世界其他地方:)

– Akash Kava
2011-3-22在16:19

@马丁:您可以使用与约翰的回答中相同的技术将它们设为私有。如果要使用实际的getter / setter,则必须使用this .__ defineGetter__或更新的Object.defineProperty函数。

–马修·克鲁姆利
2011年7月6日13:35

上面列出的方法只有一个问题,如果您想为现有类添加getter和setter,它将覆盖原型,并且原始方法将不可访问。

–xchg.ca
2011年7月20日在17:18

这种方法不会覆盖Name.prototype.constructor吗?似乎无法替代millimoose的答案。

–r0estir0bbe
16 Mar 31 '16 at 13:37

#2 楼

JavaScript中的Getter和Setters

概述

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

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和Setters

getset不是保留字,因此可以重载它们以创建自己的自定义跨浏览器计算属性功能。这可以在任何浏览器中使用。

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.barfoo.get( 'bar' )来获取“未经烹煮”的值。您可以使用条件代码来执行不同的操作,具体取决于要访问的属性的名称(通过name参数)。

Object.defineProperty()

使用Object.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支持

评论


在更紧凑的方法中,this ['_'+ name] = value;可能是this ['_'+ name] = arguments [1];并且无需指定值arg。

–雷德哈特
16 Dec 31'在10:38

示例:var foo = {bar:123,get bar(){return bar; },设置bar(value){this.bar = value; }; foo.bar = 456;引发异常:未捕获RangeError:在Object.set栏[as bar](:4:32)处,Object.set栏[as bar](:4:32)超出了最大调用堆栈大小.set bar [as bar](:4:32)在Object.set bar [as bar](:4:32)at Object.set bar [as bar](:4: 32)在Object.set bar [as bar](:4:32)

–nevf
18年5月8日在23:36



设置/获取名称必须与属性名称不同。因此,代替bar:123和this.bar = value等。例如,将其更改为_bar。请参阅:hongkiat.com/blog/getters-setters-javascript

–nevf
18年5月8日在23:43



@nevf感谢您的纠正!是的,通常具有计算属性的“真实”内部变量的名称类似于_foo或mFoo。如果它与getter / setter相同,则将由于递归而导致无限循环,然后将导致Stack Overflow™;-),因为当您说a = b时,它将调用a.get(b),后者本身将调用a = b ,它调用a.get(b),...

–Beejor
2月8日15:51

#3 楼

例如,您将使用它们来实现计算属性。

例如:

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)

评论


好的,我想我已经开始了解它了。我正在尝试为数组对象的length属性分配一个吸气剂,但收到一个错误:“声明var length”,代码看起来像这样:obj = []; obj .__ defineGetter __('length',function(){return this.length;});

–oksf
2009年5月1日20:31

这是因为Array对象已经具有内置的length属性。如果允许重新声明,则调用新长度将无限递归。尝试调用属性“ my_length”或类似的名称。

– millimoose
09年5月1日在20:52

为了在一个语句中定义两个getter,请使用Object.defineProperties。

–r0estir0bbe
16-3-31在13:39

您难道不可以使{“ area”:function(){return ...}}吗?只需将其分配为对象属性

– RegarBoy
18年7月31日在19:36

@developer这不是该语言定义的Javascript获取器,而只是一个函数。您必须调用它才能获取值,它不会超载对该属性的访问。此外,还有一个特殊的地狱圈供那些在JS中发明自己的破碎对象系统而不是在已有的对象系统基础上开发的人使用。

– millimoose
18年7月31日在20:05



#4 楼

很抱歉再次提出一个老问题,但我想我可能会提供几个非常基本的示例和伪造的解释。到目前为止,没有其他答案能够像MDN指南的第一个示例那样说明语法,该示例几乎可以说是最基本的。

字母:

br /> ...当然会记录John Smith。 getter的行为类似于变量对象属性,但提供了函数的灵活性,可以即时计算其返回值。基本上,这是创建在调用时不需要()的函数的理想方法。

设置者:

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

console.log(settings.fullname);


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

请参阅此jsfiddle,以获取更详尽,也许更实用的信息。例。将值传递到对象的设置器中会触发其他对象项的创建或填充。具体来说,在jsfiddle示例中,传递数字数组会提示设置器计算均值,中位数,众数和范围;然后为每个结果设置对象属性。

评论


我仍然不了解使用get和set与创建getMethod或setMethod的好处。没有()可以调用它的唯一好处是吗?必须将其添加到javascript中的另一个原因。

–安德烈亚斯(Andreas)
16年11月29日在22:08

@Andreas Getter和setter在调用时的行为类似于属性,可以帮助阐明其预期目的。他们不会释放原本会缺少的能力,但是它们的使用可以帮助您组织思想。那才是真正的好处。作为一个实际示例,我曾经使用吸气剂来扩展Google Maps对象。我需要计算相机的侧倾角度,以便可以将地图图块平移到地平线。 Google现在会在后端自动执行此操作;但是当时,将maps.roll作为属性而不是maps.roll()的返回值对我很有帮助。这只是一个偏爱。

–rojo
16-11-30在0:27

因此,仅使用语法糖就能使代码看起来更干净,而无需使用()。我看不到为什么您无法使用maps.roll()进行示例

–安德烈亚斯(Andreas)
16年11月30日在9:24

@安德烈亚斯谁说我不能?就像我说的那样,这只是帮助我保持思想井井有条的一种方法。编码是一门艺术。您不会问鲍勃·罗斯(Bob Ross),为什么他本来可以用橙子时却不得不用burn土。您现在可能看不到需求,但是有一天,当您决定绘画需要一点burn色时,它将出现在您的调色板上。

–rojo
16 Nov 30在11:33



:)我看到get和set语法可以做的一件事是,如果将其用作属性的属性,则会自动运行。

–安德烈亚斯(Andreas)
16-11-30在15:10



#5 楼

只有当您拥有类的私有属性时,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的有关使用Java私有成员的文章。

评论


我不同意。对于封装其定义可能不仅仅是简单变量的信息,getter和setter也是非常有用的。如果您需要更改以前使用简单属性以及可能依赖于其他属性的系统的行为,这将很方便。此外,您的示例仅演示了“伪获取器”,它们只是函数。真正的JavaScript getter以简单值的形式出现(并且无需使用函数符号即可访问),因此它们的真正功能是。

– devios1
2011年6月13日23:18



不知道我是否会称其强大。看起来像X但实际上是Y的东西不一定很清楚。我绝对不希望var baz = foo.bar背后有完整的隐藏行为集。我希望从foo.getBar()开始。

– AgmLauncher
16年7月20日在18:15

#6 楼

我认为您链接的第一篇文章很清楚地指出:


以这种方式编写JavaScript的明显优势在于,您可以使用它模糊不需要用户的值直接访问。


此处的目标是通过仅允许通过get()set()方法访问字段来封装和抽象出这些字段。这样,您可以以任何所需的方式在内部存储字段/数据,但是外部组件仅位于发布的接口之外。这样一来,您就可以在不更改外部接口的情况下进行内部更改,可以在set()方法内进行某些验证或错误检查,等等。

#7 楼

尽管我们经常习惯于在没有任何访问权限的情况下查看具有公共属性的对象,但是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


#8 楼

如此令人困惑的是... getter是在获取属性,setter或设置属性时调用的函数。
示例,如果需要

obj.prop = "abc";


您正在设置属性prop,如果您使用的是getters / setters,则将使用“ abc”作为参数来调用setter函数。
理想情况下,对象内的setter函数定义应如下所示:

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


我不确定在浏览器中实现得如何好。 Firefox似乎还有另一种语法,带有双下划线的特殊(“魔术”)方法。和往常一样,Internet Explorer不支持任何一个。

#9 楼

您可以通过构造函数的原型为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来运行此代码。

评论


这只是创建新的getName和setName方法。这些与创建属性无关!

– uzay95
17年11月16日在13:14

#10 楼

我对阅读的说明也有些困惑,因为我试图向我没有编写的现有原型添加属性,因此替换原型似乎是错误的方法。因此,为了后代,这是我向last添加Array属性的方法:

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


比添加函数IMHO稍微好一点。

#11 楼

如果您指的是访问器的概念,那么简单的目标是使底层存储不受任意操纵的影响。最极端的机制是

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();

vs.

var a = { get value(){ return 5; }; }
for (var i = 0; i < 100000; i++)
    a.value;

的性能

#12 楼

我为您准备了一个可能有些丑陋的工具,但是它确实可以跨平台完成

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检查甚至可以更疯狂:位于encodingforums.com上的type.of()代码

评论


关键是能够将属性更改为更高级的属性,而无需更改公共接口。添加call()标签会对其进行更改。

–Michael Scott Cuthbert
2013年9月25日下午5:11