将原型附加到JavaScript对象

5

假设我有以下包含人员对象的JSON。

[
  {
     "name": "Alice",
     "age": 28,
  },
  {
     "name": "Bob",
     "age": 33
  }
]

如果我解析这个,我会得到一个包含两个JavaScript对象的数组。 假设,我想给这两个对象配备一个名为introduce的方法,它会执行以下操作

function introduce() {
  return "My name is " + this.name + " and I am " + this.age + " years old.";
}

现在,我可以迭代我的两个人并调用
person.constructor.prototype.introduce = introduce

然而,这将会暴露引入函数在所有 JavaScript 对象中,而不仅仅是上面的两个对象。
我也可以手动将该函数附加到每个对象上,但如果我想要给我的人员扩展其他功能,我就必须再次迭代所有人员。
理想情况下,我希望有一个原型,我可以将其配备引入方法,然后将此原型附加到所有人员身上,这样,如果我稍后扩展原型,我的人员也将获得其他功能。
问题是:如何做到呢?

1
只需执行 person.prototype.introduce = introduce,然后删除 constructor 部分即可。 - Brian Glaz
1
为什么不直接创建一个Person类,解析JSON以创建Person。随时向Person原型添加任何函数。 - mshsayem
@BrianGlaz:我认为person是指对象本身,因此person.prototype不存在。 - cookie monster
mshsayem,我认为这有点啰嗦,似乎浪费了大量的新反序列化对象,只是为了创建具有不同原型的相同副本。让我感到困惑的是,在解析对象数组时无法指定原型。 - Niels B.
@NielsB。你可以很容易地为这个特定的目的编写一个JSON.parse的包装器,使用Object.setPrototypeOf(查看我的答案)。 - Aegis
7个回答

3
你可以使用被弃用的__proto__属性或ES6方法Object.setPrototypeOf。但需要注意的是,强烈不建议改变对象的原型(参见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto),但这似乎是一种合法的用例。
var you = { name: "Niels B." }
you.__proto__ = { hello: function () { console.log(this.name) } }
you.hello()

所以针对您的具体问题:
function Person() {}
Person.prototype = {
  introduce: function () {
    return "My name is " + this.name + " and I am " + this.age + " years old.";
  }
}

然后对于每个人:
person.__proto__ = Person.prototype;
// or
Object.setPrototypeOf(person, Person.prototype);

这是一个相关的JSPerf:http://jsperf.com/parse-json-into-an-object-and-assign-a-prototype。相关线程:https://groups.google.com/forum/#!topic/ringojs/PxS1O5jMx-8

1
这将方法添加到 Object.prototype。问题说明他不想将该方法添加到所有对象中。 - cookie monster
我在评论中更新了一个演示。使用对象字面语法创建的任何对象都将在其原型链中将Object.prototype作为第一个对象。__proto__将引用链中的第一个对象。 - cookie monster
1
@cookiemonster 嗯,你说得对,抱歉。我在Chrome控制台中尝试过,似乎并没有影响其他对象。是我的错误。 - Aegis
@cookiemonster 我编辑了代码,我认为现在它是正确的(虽然改变原型仍然是一个坏主意,除非只是一个快速的hack)。 - Aegis
1
@rgthree 感谢您指出这个问题!(尽管在我看来,使用IE的人应该死掉)。 - Aegis
显示剩余5条评论

3
我建议你创建一个类:

JSBin

var Person = (function () {
    function Person(person) {
        this.person = person;
        this.name = person.name;
        this.age = person.age;
    }
    Person.prototype.introduce = function () {
        return "My name is " + this.name + " and I am " + this.age + " years old.";
    };
    return Person;
})();

嘿!可能是个愚蠢的问题,但是将这个包裹在IIFE中的原因是什么呢? - Aegis
2
@Aegis 私有变量和方法! - Beterraba

3
你的问题有些模糊,不太容易理解。但我认为你不想让你的“人”成为一个普通对象,而是一个单独的“人类:class”。
var Person = function(obj){
    this.name = obj.name;
};

Person.prototype.introduce = function(){
  return console.log('hi, I\'m '+this.name);
};

var personA = new Person({ name:'a' });
var personB = new Person({ name:'b' });

personA.introduce();  // "hi, I'm a"
personB.introduce();  // "hi, I'm b"

1
基本上结论是,无法优雅地将原型分配给现有(字面)对象。相反,必须使用构造函数创建新对象。 - Niels B.
@NielsB。虽然这是可能的,但你不希望那样做,因为你只想让 introduce 可用于 特定 对象,比如我的例子中的 Person 实例。 - rgthree
是的,我知道。我不想替换所有对象的原型,但直觉上似乎迭代数组并替换原型引用比创建一组全新的对象更有效率。 - Niels B.

3
创建一个简单的Person类:
function Person(name,age){
    this.name = name; 
    this.age = age
}

假设你有这个数组:

var people = [
  {
     "name": "Alice",
     "age": 28,
  },
  {
     "name": "Bob",
     "age": 33
  }
];

people转换为Person数组:

people = people.map(function(p){
    return new Person(p.name,p.age)
});

将该方法添加到原型中:

Person.prototype.introduce = introduce

2
创建一个人类并使用Person.prototype:
function Person(name, age){
    this.name = name;
    this.age= age;
}

function introduce() {
  return "My name is " + this.name + " and I am " + this.age + " years old.";
}

Person.prototype.introduce = introduce;

p1 = new Person ("bob", 33);
p1.introduce();

1
我建议为这些人创建一个结构。
var Person = function Person(init){
 this.name = init.name;
 this.age = init.age;
};
Person.constructor = Person;

然后迭代构建人员。

var people = [];
for(var i = 0; i < yourJson.length; i++){
 people.push(new Person(yourJson[i]));
}

现在人们已经创建好了,也许你想让他们能够自我介绍。你可以简单地扩展你的人"类"(定义)。

Person.prototype.introduce = function() {
  return "My name is " + this.name + " and I am " + age + " years old.";
}

这将允许他们使用该函数。
for(var i = 0; i < people.length; i++){
 console.log(people[i].introduce());
}

1
如果您不想每次都重新构建,可以尝试以下方法:
var people =[
  {
     "name": "Alice",
     "age": 28,
  },
  {
     "name": "Bob",
     "age": 33
  }
],
i=-1,len=people.length,tmp
,proto={
  sayName: function(){
    console.log("from sayname",this.data.name);
  }
};
while(++i<len){
  tmp=Object.create(proto);
  tmp.data=people[i];
  people[i]=tmp;
}
people[0].sayName();
proto.sayName=function(){console.log("changed it");};
people[1].sayName();

如果你的JSON对象有许多成员,那么这可以帮助你避免在构造函数中复制它们,但我不确定你是否会获得更好的性能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接