Javascript protótipo indefinido após a desserialização eval
-
23-08-2019 - |
Pergunta
A tentativa de desserializar dados JSON e atualizar protótipo de cada objeto e herdar uma função comum.
No entanto, o seguinte script lança erro "as pessoas [0] .getFullName não é uma função". O protótipo para objetos desserializados parece ser indefinido após a atribuição.
<html>
<head>
<script>
var json = '[ {"firstName": "John", "lastName": "Smith"}, {"firstName": "Nancy", "lastName": "Jones"} ]';
var people;
eval('people = ' + json);
function Person() { }
Person.prototype.getFullName = function() {
return this.firstName + ' ' + this.lastName;
}
//assign prototype
for(var i=0; i < people.length; i++){
people[i].prototype = new Person();
}
if(people[0].getFullName() !== 'John Smith')
alert('Expected fullname to be John Smith but was ' + people[0].getFullName());
</script>
</head>
</html>
Solução
A propriedade prototype
é uma propriedade do construtores , não da casos . O que você está procurando é a propriedade __proto__
:
people[i].__proto__ = new Person();
A má notícia é que ele não funciona em todos os navegadores . Ele faz o trabalho no Firefox e no Safari, ele não funciona no IE. Uma alternativa é usar construtores para instanciar o seu leque de pessoas. Infelizmente você vai ter que copiar todas as propriedades:
function Person(obj) {
for (var property in obj) {
this[property] = obj[property];
}
return this;
}
Person.prototype.getFullName = function() {
return this.firstName + ' ' + this.lastName;
}
var people;
eval('people = ' + json);
for(var i=0; i < people.length; i++) {
people[i] = new Person(people[i]);
}
Outras dicas
Um objeto x que é criado por x = new Pessoa () está ligado a / herda a partir Person.prototype, mas, tanto quanto o padrão ECMA está preocupado que você não pode mudar x.prototype a fim a mudança que link / herança depois, esse é o "poder mágico" apenas o new palavra-chave possui.
Mozilla parece oferecer uma maneira de mudar o link objeto depois que um objeto foi criado através da propriedade não-padrão __ proto__ .
Mozilla-only:
//assign prototype
for(var i=0; i < people.length; i++){
people[i].__proto__ = Person.prototype;
}
deve funcionar em qualquer lugar:
function Person(data) { this.data = data; }
Person.prototype.getFullName = function() {
return this.data.firstName + ' ' + this.data.lastName;
}
eval('people = ' + json);
//assign prototype
for(var i=0; i < people.length; i++){
people[i] = new Person(people[i]);
}
Basicamente, você tem que obter o objeto JSON em um objeto Pessoa, e então o getFullName apenas se aplica. Eu reescrevi o que você teve um pouco de trabalho. Há provavelmente até mesmo maneiras melhores, mas eu acho que isso é o que você estava pretendendo fazer ...
<html>
<head>
<script>
//NOTE: Sending around JSON arrays leaves bad security holes for non-IE browsers (__defineSetter__)
var json = '[ {"firstName": "John", "lastName": "Smith"}, {"firstName": "Nancy", "lastName": "Jones"} ]';
//Persons is just a temporary JSON array
var persons = eval(json);
//constructor takes optional object instance and copies all properties if it gets one
function Person(person) {
if (person) {
for(var prop in person)
this[prop] = person[prop];
}
}
//Prototype applies to all Person objects
Person.prototype.getFullName = function() {
return this.firstName + ' ' + this.lastName;
}
//Create People array
var people = new Array();
for(var i=0; i < persons.length; i++){
people[i] = new Person(persons[i]);
}
//Now do your check
if(people[0].getFullName() !== 'John Smith')
alert('Expected fullname to be John Smith but was ' + people[0].getFullName());
</script>
</head>
</html>
for(var i=0; i < people.length; i++){
people[i].getFullName = Person.prototype.getFullName; }