Getters \ setters para manequins

133

Eu tenho tentado convencer os getters e setters e eles não estão afundando. Eu li Getters e Setters JavaScript e Definindo Getters e Setters e simplesmente não entendo .

Alguém pode afirmar claramente:

  1. O que um getter e um setter devem fazer, e
  2. Dê alguns exemplos MUITO simples?
Alexander Abakumov
fonte
12
Pessoalmente, eu não sei como você pode obter uma explicação mais clara do que João ...
Jason Bunting
No mundo OO, getter e ajuda setter escrever uma classe que está devidamente encapsulado
overexchange
Basicamente, eu vejo assim: você está definindo sobrecargas para obter e configurar a propriedade, e essas sobrecargas são funções; mas você não precisa ligar para eles. Desta forma, você pode substituir a = setValue(5);com a = 5;e setValue()seria assim chamado sob o capô para fazer o que quiser.
22419 Andrew Andrew

Respostas:

102

Além da resposta do @ millimoose , os setters também podem ser usados ​​para atualizar outros valores.

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

Agora, você pode definir fullNamee firste lastserá atualizado e vice-versa.

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"
Matthew Crumley
fonte
2
@ Akash: Não, porém, o Internet Explorer 9 suporta a Object.definePropertyfunção mais recente que pode definir getters e setters.
Matthew Crumley 22/03
9
Não é ele realmente doloroso que MS não apoiar JS corretamente e eles não fazem a sua corrida silverlight em todos os lugares, então eu tenho que programa tudo duas vezes, uma para SL e um para resto do mundo :)
Akash Kava
2
@ Martin: Você pode torná-los privados usando a mesma técnica da resposta de John . Se você quiser usar getters / setters reais, precisará usar this.__defineGetter__a Object.definePropertyfunção mais recente .
Matthew Crumley
1
Apenas um problema com a abordagem listada acima, se você deseja adicionar getters e setters para a classe já existente, ele substituirá os protótipos e os métodos originais não estarão acessíveis.
Xchg.ca
1
Essa abordagem não substitui Name.prototype.constructor? Parece uma péssima alternativa à resposta do millimoose .
r0estir0bbe
62

Getters e Setters em JavaScript

Visão geral

Getters e setters em JavaScript são usados ​​para definir propriedades computadas ou acessadores . Uma propriedade computada é aquela que usa uma função para obter ou definir um valor de objeto. A teoria básica está fazendo algo assim:

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'

Isso é útil para fazer coisas automaticamente nos bastidores quando uma propriedade é acessada, como manter números no intervalo, reformatar cadeias de caracteres, acionar eventos com valor alterado, atualizar dados relacionais, fornecer acesso a propriedades particulares e muito mais.

Os exemplos abaixo mostram a sintaxe básica, embora eles simplesmente obtenham e definam o valor interno do objeto sem fazer nada de especial. Nos casos do mundo real, você modificaria o valor de entrada e / ou saída para atender às suas necessidades, conforme observado acima.

obter / definir palavras-chave

O ECMAScript 5 suporta gete setpalavras - chave para definir propriedades calculadas. Eles funcionam com todos os navegadores modernos, exceto o IE 8 e abaixo.

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

Getters e setters personalizados

gete setnão são palavras reservadas, para que possam ser sobrecarregadas para criar suas próprias funções de propriedade computadas em vários navegadores. Isso funcionará em qualquer navegador.

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

Ou, para uma abordagem mais compacta, uma única função pode ser usada.

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

Evite fazer algo assim, o que pode levar ao inchaço do código.

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

Para os exemplos acima, os nomes das propriedades internas são abstraídos com um sublinhado para desencorajar os usuários de simplesmente fazer foo.barvs. foo.get( 'bar' )e obter um valor "não cozido". Você pode usar código condicional para fazer coisas diferentes, dependendo do nome da propriedade que está sendo acessada (por meio do nameparâmetro).

Object.defineProperty ()

Usar Object.defineProperty()é outra maneira de adicionar getters e setters, e pode ser usado em objetos depois de definidos. Também pode ser usado para definir comportamentos configuráveis ​​e enumeráveis. Essa sintaxe também funciona com o IE 8, mas infelizmente apenas em objetos 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 __ ()

Finalmente, __defineGetter__()é outra opção. Ele foi descontinuado, mas ainda é amplamente usado na Web e, portanto, é improvável que desapareça tão cedo. Ele funciona em todos os navegadores, exceto no IE 10 e abaixo. Embora as outras opções também funcionem bem em não-IE, essa não é tão útil.

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

Também digno de nota é que, nos últimos exemplos, os nomes internos devem ser diferentes dos nomes dos acessadores para evitar recursão (ou seja, foo.barchamando foo.get(bar)chamando foo.barchamando foo.get(bar)...).

Veja também

MDN get , set , Object.defineProperty () , __defineGetter __ () , __defineSetter __ () Suporte ao
MSDN IE8 Getter

Beejor
fonte
1
Na abordagem mais compacta , this[ '_' + name ] = value;poderia ser this[ '_' + name ] = arguments[1];e não haveria necessidade de especificar valuearg.
RedHard #
1
O exemplo: var foo = { bar : 123, get bar(){ return bar; }, set bar( value ){ this.bar = value; } }; foo.bar = 456; gera uma exceção: RangeError não detectado: tamanho máximo da pilha de chamadas excedido na barra Object.set [como barra] (<anônimo>: 4: 32) na barra Object.set [como barra] (<anônimo>: 4: 32 ) na barra Object.set [como barra] (<anonymous>: 4: 32) na barra Object.set [como barra] (<anônimo>: 4: 32) na barra Object.set [como barra] (<anônimo> : 4: 32) na barra Object.set [como barra] (<anônimo>: 4: 32)
nevf
1
O nome do conjunto / obtenção deve ser diferente do nome da propriedade. Então, em vez de bar: 123e this.bar = valueetc. altere-os para, _bar por exemplo. Veja: hongkiat.com/blog/getters-setters-javascript
nevf
@nevf Obrigado pela correção! Sim, normalmente com propriedades calculadas, a interna "real" é nomeada como _fooou mFoo. Se for o mesmo que o getter / setter, causará um loop infinito devido à recursão e, em seguida, um Stack Overflow ™ ;-) porque quando você diz a = b, chama a.get (b), que chama a = b , que chama a.get (b), ...
Beejor
58

Você os usaria, por exemplo, para implementar propriedades calculadas.

Por exemplo:

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)

millimoose
fonte
Ok, acho que estou começando a entender. Estou tentando atribuir um getter à propriedade length de um objeto de matriz, mas obtendo um erro: "Redeclaration of var length" E o código fica assim: obj = []; obj .__ defineGetter __ ('comprimento', function () {retorna this.length;});
1
Isso ocorre porque os objetos Array já possuem uma propriedade de comprimento interno. Se a redeclaração fosse permitida, chamar o novo tamanho seria repetido infinitamente. Tente chamar a propriedade "my_length" ou algo assim.
millimoose
Para definir os dois getters em uma instrução, use Object.defineProperties.
r0estir0bbe
Você não pode simplesmente criar {"area": ​​function () {return ...}}? simplesmente atribuí-la como uma propriedade do objeto
RegarBoy
@ desenvolvedor Não é um getter Javascript, conforme definido pelo idioma, é apenas uma função. Você precisa chamá-lo para obter o valor, isso não sobrecarrega o acesso à propriedade. Também há um círculo especial do inferno reservado para pessoas que inventam seus próprios sistemas de objetos quebrados em JS, em vez de desenvolver o que já possui.
millimoose
16

Desculpe ressuscitar uma pergunta antiga, mas achei que poderia contribuir com alguns exemplos muito básicos e explicações para manequins. Nenhuma das outras respostas postadas até agora ilustra a sintaxe como o primeiro exemplo do guia MDN , que é o mais básico possível.

Getter:

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

console.log(settings.fullname);

... registrará John Smith, é claro. Um getter se comporta como uma propriedade de objeto variável, mas oferece a flexibilidade de uma função para calcular seu valor retornado em tempo real. É basicamente uma maneira elegante de criar uma função que não requer () ao chamar.

Normatizador:

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

... registrará New Yorkno console. Assim como os getters, os setters são chamados com a mesma sintaxe que a configuração do valor de uma propriedade de objeto, mas são outra maneira sofisticada de chamar uma função sem ().

Veja este jsfiddle para um exemplo mais completo, talvez mais prático. A passagem de valores para o setter do objeto aciona a criação ou o preenchimento de outros itens do objeto. Especificamente, no exemplo jsfiddle, passar uma matriz de números solicita ao levantador que calcule média, mediana, modo e intervalo; depois define as propriedades do objeto para cada resultado.

rojo
fonte
Ainda não entendo o benefício de usar get e set vs criar um getMethod ou setMethod. É o único benefício que você pode chamar sem ()? Deve haver outro motivo para ele ter sido adicionado ao javascript.
Andreas
@Andreas Getters e setters se comportam como propriedades quando chamados, o que pode ajudar a articular sua finalidade. Eles não desbloqueiam habilidades ausentes, mas seu uso pode ajudá-lo a organizar seus pensamentos. Esse é o benefício real. Como exemplo prático, eu costumava usar um getter para estender um objeto do Google Maps. Eu precisava calcular o ângulo de rotação da câmera para poder girar os blocos de mapa para o horizonte. O Google faz isso automaticamente automaticamente agora; mas na época foi útil recuperar maps.rollcomo uma propriedade em vez do valor maps.roll()de retorno. É apenas uma preferência.
rojo
portanto, é apenas açúcar sintático para tornar o código mais limpo sem o (). Não consigo entender por que você não conseguiu seu exemplo commaps.roll()
Andreas
@ Andréas Quem disse que eu não poderia? Como eu disse, é apenas uma maneira de me ajudar a manter meus pensamentos organizados. Codificação é uma arte. Você não pergunta a Bob Ross por que ele teve que usar ocre queimado quando ele poderia usar laranja. Talvez você não veja uma necessidade agora, mas um dia quando decidir que sua pintura precisa de um pouco de ocre queimado, ela estará na sua paleta.
rojo
:) uma coisa que vejo que a sintaxe get e set faz é ser executada automaticamente se for usada como propriedade de uma propriedade.
Andreas
11

Getters e setters realmente só fazem sentido quando você possui propriedades particulares de classes. Como o Javascript realmente não possui propriedades de classe privada, como você normalmente pensaria em Linguagens Orientadas a Objetos, pode ser difícil de entender. Aqui está um exemplo de um objeto de contador privado. O bom desse objeto é que a variável interna "count" não pode ser acessada de fora do objeto.

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

Se você ainda estiver confuso, dê uma olhada no artigo de Crockford sobre membros privados em Javascript .

John
fonte
39
Discordo. Getters e setters também são muito úteis para encapsular informações cuja definição pode não ser apenas uma variável simples. Pode ser útil se você precisar alterar o comportamento de um sistema que anteriormente usava propriedades simples e das quais outras coisas podem depender. Além disso, seu exemplo demonstra apenas "pseudo getters", que são apenas funções. Os getters JavaScript reais aparecem como valores simples (e são acessados ​​sem notação de função), daí o poder real deles.
devios1
Não tenho certeza se eu chamaria isso de poderoso. Algo aparecendo como X, mas é realmente Y, não é necessariamente claro. Eu absolutamente NÃO esperaria var baz = foo.barter um conjunto completo de comportamentos ocultos por trás dele. Eu iria esperar que a partir foo.getBar(), no entanto.
AgmLauncher
8

Acho que o primeiro artigo ao qual você vincula afirma claramente:

A vantagem óbvia de escrever JavaScript dessa maneira é que você pode usá-lo com valores obscuros que não deseja que o usuário acesse diretamente.

O objetivo aqui é encapsular e abstrair os campos, permitindo apenas o acesso a eles através de um método get()ou set(). Dessa forma, você pode armazenar internamente o campo / dados da maneira que desejar, mas os componentes externos estão fora da interface publicada. Isso permite que você faça alterações internas sem alterar as interfaces externas, faça alguma validação ou verificação de erros dentro do set()método, etc.

matt b
fonte
6

Embora geralmente estamos acostumados a ver objetos com propriedades públicas sem nenhum controle de acesso, o JavaScript nos permite descrever com precisão as propriedades. De fato, podemos usar descritores para controlar como uma propriedade pode ser acessada e qual lógica podemos aplicar a ela. Considere o seguinte exemplo:

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: "[email protected]"
};

O resultado final:

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
Michael Horojanski
fonte
2

O que é tão confuso sobre isso ... getters são funções que são chamadas quando você obtém uma propriedade, setters, quando você a define. exemplo, se você fizer

obj.prop = "abc";

Você está configurando a propriedade prop, se estiver usando getters / setters, a função setter será chamada, com "abc" como argumento. A definição da função setter dentro do objeto seria idealmente parecida com esta:

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

Não tenho certeza de quão bem isso é implementado nos navegadores. Parece que o Firefox também possui uma sintaxe alternativa, com métodos especiais com sublinhado duplo ("mágico"). Como de costume, o Internet Explorer não suporta nada disso.

Rolf
fonte
2

Você pode definir o método de instância para a classe js, através do protótipo do construtor.

A seguir está o código de exemplo:

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

E, isso deve funcionar para qualquer navegador, você também pode simplesmente usar o nodejs para executar este código.

Eric Wang
fonte
1
Isso está apenas criando novos métodos getName e setName. Eles não estão relacionados à criação de propriedade!
uzay95
2

Também fiquei um pouco confuso com a explicação que li , porque estava tentando adicionar uma propriedade a um protótipo existente que não escrevi; portanto, substituir o protótipo parecia a abordagem errada. Portanto, para a posteridade, veja como adicionei uma lastpropriedade a Array:

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

Sempre um pouco melhor do que adicionar uma função IMHO.

shawkinaw
fonte
1

Se você está se referindo ao conceito de acessadores, o objetivo simples é ocultar o armazenamento subjacente da manipulação arbitrária. O mecanismo mais extremo para isso é

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

Se você está se referindo ao recurso JS getter / setter real, por exemplo. defineGetter/ defineSetter, ou { get Foo() { /* code */ } }, então, vale a pena notar que, na maioria dos mecanismos modernos, o uso subsequente dessas propriedades será muito mais lento do que seria. por exemplo. comparar o desempenho de

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;
olliej
fonte
-1

Eu tenho um para vocês que pode ser um pouco feio, mas é feito em várias plataformas

function myFunc () {

var _myAttribute = "default";

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

Dessa forma, quando você liga

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

Se você realmente deseja apimentar as coisas, insira um tipo de verificação:

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

ou fique ainda mais louco com o código typeof check avançado: type.of () em codingforums.com

artsy.ca
fonte
o objetivo era poder alterar um atributo para algo mais sofisticado sem precisar alterar a interface pública. A adição de uma marca de chamada () a altera.
Michael Scott Cuthbert