Como estender a função com classes ES6?


105

ES6 permite estender objetos especiais. Portanto, é possível herdar da função. Esse objeto pode ser chamado como uma função, mas como posso implementar a lógica para essa chamada?

class Smth extends Function {
  constructor (x) {
    // What should be done here
    super();
  }
}

(new Smth(256))() // to get 256 at this call?

Qualquer método de classe obtém referência à instância de classe via this. Mas quando é chamado como uma função, thisrefere-se a window. Como posso obter a referência à instância da classe quando ela é chamada como uma função?

PS: Mesma pergunta em russo.


17
Ah, finalmente alguém perguntou esta pergunta :-)
Bergi

1
Basta fazer super(x)(ou seja, passá-lo para Function)? Não tenho certeza se Functionrealmente pode ser estendido.
Felix Kling

Lembre-se de que ainda há problemas com a extensão de classes integradas. A especificação sugere que isso deve ser possível, mas tive problemas de extensão Error, entre outros.
ssube

1
Lembre-se de que Functioné simplesmente um construtor de função. A implementação da função deve ser passada para o construtor. Se você não quiser Smthaceitar uma implementação, você tem que fornecê-la no construtor, ou seja super('function implementation here').
Felix Kling

1
@Qwertiy: Eu diria que este é a exceção , não o caso geral. Isso também é muito específico para expressões de função , mas você está usando o Functionconstrutor (tempo de execução), que é muito diferente de uma expressão de função (sintaxe).
Felix Kling

Respostas:


49

A superchamada invocará o Functionconstrutor, que espera uma string de código. Se quiser acessar seus dados de instância, você pode apenas codificá-los:

class Smth extends Function {
  constructor(x) {
    super("return "+JSON.stringify(x)+";");
  }
}

mas isso não é muito satisfatório. Queremos usar um fechamento.

Ter a função retornada como um fechamento que pode acessar suas variáveis ​​de instância é possível, mas não é fácil. A boa notícia é que você não precisa chamar superse não quiser - você ainda pode returnarbitrar objetos de seus construtores de classe ES6. Neste caso, faríamos

class Smth extends Function {
  constructor(x) {
    // refer to `smth` instead of `this`
    function smth() { return x; };
    Object.setPrototypeOf(smth, Smth.prototype);
    return smth;
  }
}

Mas podemos fazer ainda melhor e abstrair essa coisa de Smth:

class ExtensibleFunction extends Function {
  constructor(f) {
    return Object.setPrototypeOf(f, new.target.prototype);
  }
}

class Smth extends ExtensibleFunction {
  constructor(x) {
    super(function() { return x; }); // closure
    // console.log(this); // function() { return x; }
    // console.log(this.prototype); // {constructor: …}
  }
}
class Anth extends ExtensibleFunction {
  constructor(x) {
    super(() => { return this.x; }); // arrow function, no prototype object created
    this.x = x;
  }
}
class Evth extends ExtensibleFunction {
  constructor(x) {
    super(function f() { return f.x; }); // named function
    this.x = x;
  }
}

É certo que isso cria um nível adicional de indireção na cadeia de herança, mas isso não é necessariamente uma coisa ruim (você pode estendê-lo em vez do nativo Function). Se você quiser evitá-lo, use

function ExtensibleFunction(f) {
  return Object.setPrototypeOf(f, new.target.prototype);
}
ExtensibleFunction.prototype = Function.prototype;

mas observe que Smthnão herdará Functionpropriedades estáticas dinamicamente .


Quero obter acesso ao estado da classe da função.
Qwertiy

2
@Qwertiy: Então use a segunda sugestão de Bergi.
Felix Kling

@ AlexanderO'Mara: Você não pode mudar o protótipo da função se quiser que suas Smthinstâncias sejam instanceof Smth(como todos esperariam). Você pode omitir a Object.setPrototypeOfchamada se não precisar deste ou de qualquer um dos métodos de protótipo declarados em sua classe.
Bergi

@ AlexanderO'Mara: Também Object.setPrototypeOfnão é muito perigoso para a otimização, desde que seja feito logo após a criação do objeto. Apenas se você alterar o [[protótipo]] de um objeto para frente e para trás durante sua vida útil, ele ficará ruim.
Bergi

1
@amn Não, você não, quando você não usa thise returnum objeto.
Bergi

32

Esta é uma abordagem para criar objetos que podem ser chamados que referenciam corretamente seus membros de objeto e mantêm a herança correta, sem mexer com protótipos.

Simplesmente:

class ExFunc extends Function {
  constructor() {
    super('...args', 'return this.__self__.__call__(...args)')
    var self = this.bind(this)
    this.__self__ = self
    return self
  }

  // Example `__call__` method.
  __call__(a, b, c) {
    return [a, b, c];
  }
}

Estenda esta classe e adicione um __call__método, mais abaixo ...

Uma explicação no código e comentários:

// This is an approach to creating callable objects
// that correctly reference their own object and object members,
// without messing with prototypes.

// A Class that extends Function so we can create
// objects that also behave like functions, i.e. callable objects.
class ExFunc extends Function {
  constructor() {
    super('...args', 'return this.__self__.__call__(...args)');
    // Here we create a function dynamically using `super`, which calls
    // the `Function` constructor which we are inheriting from. Our aim is to create
    // a `Function` object that, when called, will pass the call along to an internal
    // method `__call__`, to appear as though the object is callable. Our problem is
    // that the code inside our function can't find the `__call__` method, because it
    // has no reference to itself, the `this` object we just created.
    // The `this` reference inside a function is called its context. We need to give
    // our new `Function` object a `this` context of itself, so that it can access
    // the `__call__` method and any other properties/methods attached to it.
    // We can do this with `bind`:
    var self = this.bind(this);
    // We've wrapped our function object `this` in a bound function object, that
    // provides a fixed context to the function, in this case itself.
    this.__self__ = self;
    // Now we have a new wrinkle, our function has a context of our `this` object but
    // we are going to return the bound function from our constructor instead of the
    // original `this`, so that it is callable. But the bound function is a wrapper
    // around our original `this`, so anything we add to it won't be seen by the
    // code running inside our function. An easy fix is to add a reference to the
    // new `this` stored in `self` to the old `this` as `__self__`. Now our functions
    // context can find the bound version of itself by following `this.__self__`.
    self.person = 'Hank'
    return self;
  }
  
  // An example property to demonstrate member access.
  get venture() {
    return this.person;
  }
  
  // Override this method in subclasses of ExFunc to take whatever arguments
  // you want and perform whatever logic you like. It will be called whenever
  // you use the obj as a function.
  __call__(a, b, c) {
    return [this.venture, a, b, c];
  }
}

// A subclass of ExFunc with an overridden __call__ method.
class DaFunc extends ExFunc {
  constructor() {
    super()
    this.a = 'a1'
    this.b = 'b2'
    this.person = 'Dean'
  }

  ab() {
    return this.a + this.b
  }
  
  __call__(ans) {
    return [this.ab(), this.venture, ans];
  }
}

// Create objects from ExFunc and its subclass.
var callable1 = new ExFunc();
var callable2 = new DaFunc();

// Inheritance is correctly maintained.
console.log('\nInheritance maintained:');
console.log(callable2 instanceof Function);  // true
console.log(callable2 instanceof ExFunc);  // true
console.log(callable2 instanceof DaFunc);  // true

// Test ExFunc and its subclass objects by calling them like functions.
console.log('\nCallable objects:');
console.log( callable1(1, 2, 3) );  // [ 'Hank', 1, 2, 3 ]
console.log( callable2(42) );  // [ 'a1b2', Dean', 42 ]

// Test property and method access
console.log(callable2.a, callable2.b, callable2.ab())

Ver em repl.it

Explicações adicionais sobre bind:

function.bind()funciona muito parecido function.call()e eles compartilham uma assinatura de método semelhante:

fn.call(this, arg1, arg2, arg3, ...);mais em mdn

fn.bind(this, arg1, arg2, arg3, ...);mais em mdn

Em ambos, o primeiro argumento redefine o thiscontexto dentro da função. Argumentos adicionais também podem ser associados a um valor. Mas onde callimediatamente chama a função com os valores vinculados, bindretorna um objeto de função "exótico" que envolve o original de forma transparente, com thise quaisquer argumentos predefinidos.

Então, quando você define uma função, bindalguns de seus argumentos:

var foo = function(a, b) {
  console.log(this);
  return a * b;
}

foo = foo.bind(['hello'], 2);

Você chama a função vinculada apenas com os argumentos restantes, seu contexto é predefinido, neste caso, para ['hello'].

// We pass in arg `b` only because arg `a` is already set.
foo(2);  // returns 4, logs `['hello']`

Você pode adicionar uma explicação de por que bindfunciona (ou seja, por que ele retorna uma instância de ExFunc)?
Bergi

@Bergi bindretorna um objeto de função transparente que envolve o objeto de função no qual foi chamado, que é nosso objeto chamável, apenas com o thisrebote do contexto. Portanto, ele realmente retorna uma instância encapsulada de forma transparente de ExFunc. Postagem atualizada com mais informações sobre bind.
Adrien

1
@Bergi Todos os getters / setters e métodos são acessíveis, propriedades / atributos devem ser atribuídos constructordepois bindem ExFunc. Em subclasses de ExFunc, todos os membros são acessíveis. Quanto a instanceof; em es6, as funções associadas são chamadas de exóticas, portanto, seus funcionamentos internos não são aparentes, mas estou pensando que ela passa a chamada para seu destino empacotado, via Symbol.hasInstance. É muito parecido com um Proxy, mas é um método simples para obter o efeito desejado. Sua assinatura é semelhante, mas não a mesma.
Adrien

1
@Adrien mas de dentro __call__não consigo acessar this.aou this.ab(). por exemplo, repl.it/repls/FelineFinishedDesktopenvironment
roubar

1
@rob bem localizado, há um erro de referência, atualizei a resposta e o código com uma correção e uma nova explicação.
Adrien de

20

Você pode envolver a instância Smth em um Proxy com uma apply(e talvez construct) armadilha:

class Smth extends Function {
  constructor (x) {
    super();
    return new Proxy(this, {
      apply: function(target, thisArg, argumentsList) {
        return x;
      }
    });
  }
}
new Smth(256)(); // 256

Ótima ideia. Como isso. Devo implementar alguma lógica mais em vez de colocar dentro de aplicar?
Qwertiy

4
Um proxy incorreria em alguma sobrecarga, não é? Além disso, thisainda é uma função vazia (verificar new Smth().toString()).
Bergi

2
@Bergi Não faço ideia sobre desempenho. O MDN tem um grande aviso em negrito vermelho setPrototypeOfe não diz nada sobre proxies. Mas acho que os proxies podem ser tão problemáticos quanto setPrototypeOf. E sobre toString, ele pode ser sombreado com um método personalizado em Smth.prototype. O nativo é dependente da implementação de qualquer maneira.
Oriol

@Qwertiy Você pode adicionar uma constructarmadilha para especificar o comportamento de new new Smth(256)(). E adicione métodos personalizados que sombreiam os nativos que acessam o código de uma função, como toStringBergi observou.
Oriol

I mento é o seu applymétodo implementado da maneira que deve ser usado, ou é apenas uma demonstração e eu preciso procurar mais informações Proxye Reflectusá-lo da maneira adequada?
Qwertiy

3

Segui o conselho da resposta de Bergi e envolvi-o em um módulo NPM .

var CallableInstance = require('callable-instance');

class ExampleClass extends CallableInstance {
  constructor() {
    // CallableInstance accepts the name of the property to use as the callable
    // method.
    super('instanceMethod');
  }

  instanceMethod() {
    console.log("instanceMethod called!");
  }
}

var test = new ExampleClass();
// Invoke the method normally
test.instanceMethod();
// Call the instance itself, redirects to instanceMethod
test();
// The instance is actually a closure bound to itself and can be used like a
// normal function.
test.apply(null, [ 1, 2, 3 ]);

3

Atualizar:

Infelizmente, isso não funciona muito bem porque agora está retornando um objeto de função em vez de uma classe, então parece que isso realmente não pode ser feito sem modificar o protótipo. Lame.


Basicamente, o problema é que não há como definir o thisvalor do Functionconstrutor. A única maneira de realmente fazer isso seria usar o .bindmétodo posteriormente; no entanto, ele não é muito amigável.

Poderíamos fazer isso em uma classe base auxiliar, no entanto this, não se torna disponível até depois da superchamada inicial , por isso é um pouco complicado.

Exemplo de trabalho:

'use strict';

class ClassFunction extends function() {
    const func = Function.apply(null, arguments);
    let bound;
    return function() {
        if (!bound) {
            bound = arguments[0];
            return;
        }
        return func.apply(bound, arguments);
    }
} {
    constructor(...args) {
        (super(...args))(this);
    }
}

class Smth extends ClassFunction {
    constructor(x) {
        super('return this.x');
        this.x = x;
    }
}

console.log((new Smth(90))());

(O exemplo requer um navegador moderno ou node --harmony.)

Basicamente, a função base ClassFunctionextends envolverá a Functionchamada do construtor com uma função customizada que é semelhante .bind, mas permite a vinculação mais tarde, na primeira chamada. Em seguida, no ClassFunctionpróprio construtor, ele chama a função retornada da superqual agora é a função vinculada, passando thispara concluir a configuração da função de vinculação personalizada.

(super(...))(this);

Isso tudo é um pouco complicado, mas evita a mutação do protótipo, que é considerado formato incorreto por motivos de otimização e pode gerar avisos nos consoles do navegador.


1
Você está complicando as coisas. boundirá se referir à função que você returndaquela classe anônima. Basta nomeá-lo e consultá-lo diretamente. Eu também recomendaria evitar passar strings de código, elas são apenas uma bagunça para trabalhar (em cada etapa do processo de desenvolvimento).
Bergi

Isso extendsrealmente não parece funcionar como esperado, pois Function.isPrototypeOf(Smth)e também new Smth instanceof Functionsão falsos.
Bergi

@Bergi Qual mecanismo JS você está usando? console.log((new Smth) instanceof Function);é truepara mim no Node v5.11.0 e no Firefox mais recente.
Alexander O'Mara

Opa, exemplo errado. É new Smth instanceof Smthque não está funcionando com sua solução. Além disso, nenhum método de Smthestará disponível em suas instâncias - já que você apenas retorna um padrão Function, não um Smth.
Bergi

1
@Bergi Droga, parece que você está certo. No entanto, a extensão de qualquer tipo nativo parece ter o mesmo problema. extend Functiontambém torna new Smth instanceof Smthfalso.
Alexander O'Mara

1

Em primeiro lugar, cheguei a uma solução com arguments.callee, mas foi horrível.
Eu esperava que ele quebrasse no modo estrito global, mas parece que funciona mesmo aí.

class Smth extends Function {
  constructor (x) {
    super('return arguments.callee.x');
    this.x = x;
  }
}

(new Smth(90))()

Era um péssimo jeito por usar arguments.callee, passar o código como string e forçar sua execução em modo não estrito. Mas então a ideia de substituir applyapareceu.

var global = (1,eval)("this");

class Smth extends Function {
  constructor(x) {
    super('return arguments.callee.apply(this, arguments)');
    this.x = x;
  }
  apply(me, [y]) {
    me = me !== global && me || this;
    return me.x + y;
  }
}

E o teste, mostrando que sou capaz de executar essa função de diferentes maneiras:

var f = new Smth(100);

[
f instanceof Smth,
f(1),
f.call(f, 2),
f.apply(f, [3]),
f.call(null, 4),
f.apply(null, [5]),
Function.prototype.apply.call(f, f, [6]),
Function.prototype.apply.call(f, null, [7]),
f.bind(f)(8),
f.bind(null)(9),
(new Smth(200)).call(new Smth(300), 1),
(new Smth(200)).apply(new Smth(300), [2]),
isNaN(f.apply(window, [1])) === isNaN(f.call(window, 1)),
isNaN(f.apply(window, [1])) === isNaN(Function.prototype.apply.call(f, window, [1])),
] == "true,101,102,103,104,105,106,107,108,109,301,302,true,true"

Versão com

super('return arguments.callee.apply(arguments.callee, arguments)');

na verdade contém bindfuncionalidade:

(new Smth(200)).call(new Smth(300), 1) === 201

Versão com

super('return arguments.callee.apply(this===(1,eval)("this") ? null : this, arguments)');
...
me = me || this;

torna calle applyem windowinconsistentes:

isNaN(f.apply(window, [1])) === isNaN(f.call(window, 1)),
isNaN(f.apply(window, [1])) === isNaN(Function.prototype.apply.call(f, window, [1])),

portanto, o cheque deve ser movido para apply:

super('return arguments.callee.apply(this, arguments)');
...
me = me !== global && me || this;

1
O que realmente você está tentando fazer?
Obrigado

2
Acho que as aulas estão sempre no modo estrito: stackoverflow.com/questions/29283935/…
Alexander O'Mara

@AlexanderO'Mara, aliás, thisé janela, não indefinida, portanto a função criada não está em modo estrito (pelo menos em cromo).
Qwertiy

Por favor, pare de downwoting esta resposta. Já escrevi que é um mau caminho. Mas é realmente uma resposta - funciona tanto no FF quanto no Chrome (não tem o Edge para verificar).
Qwertiy

Acho que isso funciona porque Functionnão está no modo estrito. Embora horrível, é +1 interessante. Você provavelmente não seria capaz de andar mais uma corrente embora.
Alexander O'Mara

1

Esta é a solução que desenvolvi que atende a todas as minhas necessidades de extensão de funções e me serviu muito bem. Os benefícios desta técnica são:

  • Ao estender ExtensibleFunction, o código é idiomático de estender qualquer classe ES6 (não, mexer com construtores ou proxies fingidos).
  • A cadeia de protótipos é mantida em todas as subclasses e instanceof/ .constructorretorna os valores esperados.
  • .bind() .apply()e .call()todos funcionam conforme o esperado. Isso é feito substituindo esses métodos para alterar o contexto da função "interna" em oposição à ExtensibleFunctioninstância (ou de sua subclasse).
  • .bind()retorna uma nova instância do construtor de funções (seja ele ExtensibleFunctionou uma subclasse). Ele usa Object.assign()para garantir que as propriedades armazenadas na função associada sejam consistentes com as da função de origem.
  • Os fechamentos são respeitados e as funções das setas continuam a manter o contexto adequado.
  • A função "interna" é armazenada por meio de um Symbol, que pode ser ofuscado por módulos ou um IIFE (ou qualquer outra técnica comum de privatizar referências).

E sem mais delongas, o código:

// The Symbol that becomes the key to the "inner" function 
const EFN_KEY = Symbol('ExtensibleFunctionKey');

// Here it is, the `ExtensibleFunction`!!!
class ExtensibleFunction extends Function {
  // Just pass in your function. 
  constructor (fn) {
    // This essentially calls Function() making this function look like:
    // `function (EFN_KEY, ...args) { return this[EFN_KEY](...args); }`
    // `EFN_KEY` is passed in because this function will escape the closure
    super('EFN_KEY, ...args','return this[EFN_KEY](...args)');
    // Create a new function from `this` that binds to `this` as the context
    // and `EFN_KEY` as the first argument.
    let ret = Function.prototype.bind.apply(this, [this, EFN_KEY]);
    // For both the original and bound funcitons, we need to set the `[EFN_KEY]`
    // property to the "inner" function. This is done with a getter to avoid
    // potential overwrites/enumeration
    Object.defineProperty(this, EFN_KEY, {get: ()=>fn});
    Object.defineProperty(ret, EFN_KEY, {get: ()=>fn});
    // Return the bound function
    return ret;
  }

  // We'll make `bind()` work just like it does normally
  bind (...args) {
    // We don't want to bind `this` because `this` doesn't have the execution context
    // It's the "inner" function that has the execution context.
    let fn = this[EFN_KEY].bind(...args);
    // Now we want to return a new instance of `this.constructor` with the newly bound
    // "inner" function. We also use `Object.assign` so the instance properties of `this`
    // are copied to the bound function.
    return Object.assign(new this.constructor(fn), this);
  }

  // Pretty much the same as `bind()`
  apply (...args) {
    // Self explanatory
    return this[EFN_KEY].apply(...args);
  }

  // Definitely the same as `apply()`
  call (...args) {
    return this[EFN_KEY].call(...args);
  }
}

/**
 * Below is just a bunch of code that tests many scenarios.
 * If you run this snippet and check your console (provided all ES6 features
 * and console.table are available in your browser [Chrome, Firefox?, Edge?])
 * you should get a fancy printout of the test results.
 */

// Just a couple constants so I don't have to type my strings out twice (or thrice).
const CONSTRUCTED_PROPERTY_VALUE = `Hi, I'm a property set during construction`;
const ADDITIONAL_PROPERTY_VALUE = `Hi, I'm a property added after construction`;

// Lets extend our `ExtensibleFunction` into an `ExtendedFunction`
class ExtendedFunction extends ExtensibleFunction {
  constructor (fn, ...args) {
    // Just use `super()` like any other class
    // You don't need to pass ...args here, but if you used them
    // in the super class, you might want to.
    super(fn, ...args);
    // Just use `this` like any other class. No more messing with fake return values!
    let [constructedPropertyValue, ...rest] = args;
    this.constructedProperty = constructedPropertyValue;
  }
}

// An instance of the extended function that can test both context and arguments
// It would work with arrow functions as well, but that would make testing `this` impossible.
// We pass in CONSTRUCTED_PROPERTY_VALUE just to prove that arguments can be passed
// into the constructor and used as normal
let fn = new ExtendedFunction(function (x) {
  // Add `this.y` to `x`
  // If either value isn't a number, coax it to one, else it's `0`
  return (this.y>>0) + (x>>0)
}, CONSTRUCTED_PROPERTY_VALUE);

// Add an additional property outside of the constructor
// to see if it works as expected
fn.additionalProperty = ADDITIONAL_PROPERTY_VALUE;

// Queue up my tests in a handy array of functions
// All of these should return true if it works
let tests = [
  ()=> fn instanceof Function, // true
  ()=> fn instanceof ExtensibleFunction, // true
  ()=> fn instanceof ExtendedFunction, // true
  ()=> fn.bind() instanceof Function, // true
  ()=> fn.bind() instanceof ExtensibleFunction, // true
  ()=> fn.bind() instanceof ExtendedFunction, // true
  ()=> fn.constructedProperty == CONSTRUCTED_PROPERTY_VALUE, // true
  ()=> fn.additionalProperty == ADDITIONAL_PROPERTY_VALUE, // true
  ()=> fn.constructor == ExtendedFunction, // true
  ()=> fn.constructedProperty == fn.bind().constructedProperty, // true
  ()=> fn.additionalProperty == fn.bind().additionalProperty, // true
  ()=> fn() == 0, // true
  ()=> fn(10) == 10, // true
  ()=> fn.apply({y:10}, [10]) == 20, // true
  ()=> fn.call({y:10}, 20) == 30, // true
  ()=> fn.bind({y:30})(10) == 40, // true
];

// Turn the tests / results into a printable object
let table = tests.map((test)=>(
  {test: test+'', result: test()}
));

// Print the test and result in a fancy table in the console.
// F12 much?
console.table(table);

Editar

Já que estava com vontade, decidi publicar um pacote para isso no npm.


1

Há uma solução simples que tira proveito dos recursos funcionais do JavaScript: Passe a "lógica" como um argumento-função para o construtor de sua classe, atribua os métodos dessa classe a essa função e, em seguida, retorne essa função do construtor como resultado :

class Funk
{
    constructor (f)
    { let proto       = Funk.prototype;
      let methodNames = Object.getOwnPropertyNames (proto);
      methodNames.map (k => f[k] = this[k]);
      return f;
    }

    methodX () {return 3}
}

let myFunk  = new Funk (x => x + 1);
let two     = myFunk(1);         // == 2
let three   = myFunk.methodX();  // == 3

O acima foi testado em Node.js 8.

Uma deficiência do exemplo acima é que ele não suporta métodos herdados da cadeia da superclasse. Para oferecer suporte a isso, basta substituir "Object. GetOwnPropertyNames (...)" por algo que retorna também os nomes dos métodos herdados. Acredito que como fazer isso está explicado em alguma outra pergunta-resposta no Stack Overflow :-). BTW. Seria bom se o ES7 adicionasse um método para produzir nomes de métodos herdados também ;-).

Se você precisar suportar métodos herdados, uma possibilidade é adicionar um método estático à classe acima que retorna todos os nomes de métodos herdados e locais. Em seguida, chame isso do construtor. Se você estender essa classe Funk, também obterá aquele método estático herdado junto.


Acho que este exemplo dá uma resposta simples à pergunta original "... como posso implementar a lógica para tal chamada". Basta passá-lo como um argumento com valor de função para o construtor. No código acima, a classe Funk não estende explicitamente Function, embora pudesse, na verdade não precisa. Como você pode ver, pode chamar suas "instâncias" apenas como chama qualquer função comum.
Panu Logic
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.