34

たとえば、 Person オブジェクトのデータなど、サーバーから JSON オブジェクトを受け取ったとします。

{firstName: "Bjarne", lastName: "Fisk"}

ここで、たとえば fullName を計算するために、これらのデータの上にいくつかのメソッドが必要です。

fullName: function() { return this.firstName + " " + this.lastName; }

私ができるように

var personData = {firstName: "Bjarne", lastName: "Fisk"};
var person = PROFIT(personData);
person.fullName(); // => "Bjarne Fisk"

ここで基本的にやりたいことは、オブジェクトのプロトタイプにメソッドを追加することです。このfullName()メソッドは一般的なものであるため、データ オブジェクト自体に追加しないでください。お気に入り..:

personData.fullName = function() { return this.firstName + " " + this.lastName; }

... 多くの冗長性が発生します。そして間違いなくデータオブジェクトを「汚染」します。

そのようなメソッドを単純なデータ オブジェクトに追加する現在のベスト プラクティスの方法は何ですか?

編集:

トピックから少し外れますが、上記の問題が解決できれば、次のpattern matchingような素敵な疑似処理を行うことができます。

if ( p = Person(data) ) {
   console.log(p.fullName());
} else if ( d = Dog(data) ) {
   console.log("I'm a dog lol. Hear me bark: "+d.bark());
} else {
   throw new Exception("Shitty object");
}

Personオブジェクトが適切な属性を持っている場合、メソッドをDog追加します。dataそうでない場合は、falsy を返します (つまり、データが一致しない/一致しない)。

ボーナス質問:これを使用または有効にする(つまり、簡単にする)ライブラリを知っている人はいますか?それはすでにjavascriptパターンですか?もしそうなら、それは何と呼ばれていますか; 詳細なリンクはありますか?ありがとう :)

4

9 に答える 9

24

Object が、サーバー出力を解析して Object を生成する JSON ライブラリから取得されたものであると仮定すると、一般に、prototype に特別なものはありません。異なるサーバー応答に対して生成された 2 つのオブジェクトは、プロトタイプ チェーンを共有しません (もちろん、Object.prototype 以外に;))

JSON から「Person」が作成されるすべての場所を制御する場合、逆の方法で行うことができます。「空の」Person オブジェクトを作成し (プロトタイプに fullName のようなメソッドを使用)、生成されたオブジェクトでそれを拡張します。 JSON から ($.extend、_.extend、または類似のものを使用)。

var p = { first : "John", last : "Doe"};

function Person(data) {
   _.extend(this, data);
}

Person.prototype.fullName = function() {
   return this.first + " " + this.last;   
}

console.debug(new Person(p).fullName());
于 2013-02-24T18:00:47.067 に答える
9

ここには別の可能性があります。 JSON.parseは、2番目のパラメーターを受け入れます。これは、検出されたオブジェクトをリーフノードからルートノードに復活させるために使用される関数です。したがって、固有のプロパティに基づいて型を認識できる場合は、リバイバー関数で型を作成できます。これを行う非常に簡単な例を次に示します。

var MultiReviver = function(types) {
    // todo: error checking: types must be an array, and each element
    //       must have appropriate `test` and `deserialize` functions
    return function(key, value) {
        var type;
        for (var i = 0; i < types.length; i++) {
            type = types[i];
            if (type.test(value)) {
                return type.deserialize(value);
            }
        }
        return value;
    };
};

var Person = function(first, last) {
    this.firstName = first;
    this.lastName = last;
};
Person.prototype.fullName = function() {
    return this.firstName + " " + this.lastName;
};
Person.prototype.toString = function() {return "Person: " + this.fullName();};
Person.test = function(value) {
    return typeof value.firstName == "string" && 
           typeof value.lastName == "string";
};
Person.deserialize = function(obj) {
    return new Person(obj.firstName, obj.lastName);
};

var Dog = function(breed, name) {
    this.breed = breed;
    this.name = name;
}
Dog.prototype.species = "canine";
Dog.prototype.toString = function() {
    return this.breed + " named " + this.name;
};
Dog.test = function(value) {return value.species === "canine";};
Dog.deserialize = function(obj) {return new Dog(obj.breed, obj.name);};


var reviver = new MultiReviver([Person, Dog]);

var text = '[{"firstName": "John", "lastName": "Doe"},' +
            '{"firstName": "Jane", "lastName": "Doe"},' +
            '{"firstName": "Junior", "lastName": "Doe"},' +
            '{"species": "canine", "breed": "Poodle", "name": "Puzzle"},' +
            '{"species": "canine", "breed": "Wolfhound", "name": "BJ"}]';

var family = JSON.parse(text, reviver)
family.join("\n");

// Person: John Doe
// Person: Jane Doe
// Person: Junior Doe
// Poodle named Puzzle
// Wolfhound named BJ

これは、タイプを明確に認識できるかどうかに依存します。たとえば、firstNameプロパティとlastNameプロパティも持つPersonのサブタイプでさえ、他のタイプがある場合、これは機能しません。しかし、それはいくつかのニーズをカバーするかもしれません。

于 2013-02-24T19:44:26.833 に答える
5

プレーンな JSON データを扱っている場合、各 person オブジェクトのプロトタイプは単純にObject.prototype. プロトタイプを持つオブジェクトにするには、Person.prototypeまずPersonコンストラクターとプロトタイプが必要です (従来の方法で Javascript OOP を実行していると仮定します)。

function Person() {
    this.firstName = null;
    this.lastName = null;
}
Person.prototype.fullName = function() { return this.firstName + " " + this.lastName; }

次に、単純なオブジェクトを Person オブジェクトに変換する方法が必要になります。たとえば、mixinあるオブジェクトから別のオブジェクトにすべてのプロパティを単純にコピーする関数が呼び出された場合、次のようにすることができます。

//example JSON object
var jsonPerson = {firstName: "Bjarne", lastName: "Fisk"};

var person = new Person();
mixin(person, jsonPerson);

これは問題を解決する 1 つの方法にすぎませんが、いくつかのアイデアが得られることを願っています。


更新:Object.assign()最新のブラウザーで利用できるようになったので、独自の mixin 関数を作成する代わりにそれを使用できます。Object.assign()古いブラウザで動作させるための shim もあります。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfillを参照してください。

于 2013-02-24T18:03:43.560 に答える
4

おそらくこれを行うべきではありません。

JSON を使用すると、型ではなく状態をシリアル化できます。したがって、ユースケースでは、次のようにする必要があります。

var Person = function ( data ) {
    if ( data ) {
        this.firstName = data.firstName;
        this.lastName = data.lastName;
    }
};

Person.prototype.fullName = function ( ) {
    return this.firstName + ' ' + this.lastName;
};

//

var input = '{"firstName":"john", "lastName":"Doe"}';
var myData = JSON.parse( input );
var person = new Person( myData );
于 2013-02-24T18:06:18.083 に答える
3

つまり、既存のオブジェクトのプロトタイプ (別名クラス) を変更したいということです。技術的には、次の方法で実行できます。

var Person = {
  function fullName() { return this.firstName + " " + this.lastName; }
};

// that is your PROFIT function body: 
personData.__proto__ = Person ;

trueその後乗ればpersonData instanceof Person

于 2013-02-24T18:16:55.650 に答える
1

新しいっぽい Object.setPrototypeOf()を使用します。(現在、IE11 および他のすべてのブラウザーでサポートされています。)

fullName() など、必要なメソッドを含むクラス/プロトタイプを作成してから、

Object.setPrototypeOf( personData, Person.prototype );

警告 (上記の MDN ページにリンクされています) が示唆しているように、この関数は軽々しく使用されるべきではありませんが、既存のオブジェクトのプロトタイプを変更する場合には意味があり、それが目的のようです。

于 2016-07-02T14:59:14.847 に答える
0

匿名オブジェクトにはプロトタイプがありません。なぜこれを持っていないのですか:

function fullName(obj) {
    return obj.firstName + ' ' + obj.lastName;
}

fullName(person);

関数呼び出しの代わりにメソッド呼び出しを絶対に使用する必要がある場合は、いつでも同様のことを行うことができますが、オブジェクトを使用します。

var Person = function (person) { this.person = person; }
Person.prototype.fullName = function () {
    return this.person.firstName + ' ' + this.person.lastName;
}
var person = new Person(personData);
person.fullName();
于 2013-02-24T18:04:00.783 に答える
0

ベアボーン オブジェクトにカスタム メソッドをバインドするために、プロトタイプを使用する必要はありません。

ここに、冗長なコードを避けてコードを汚染しないエレガントな例があります

var myobj = {
  title: 'example',
  assets: 
  {
    resources: ['zero', 'one', 'two']
  }
}

var myfunc = function(index)
{
    console.log(this.resources[index]); 
}

myobj.assets.giveme = myfunc

myobj.assets.giveme(1);

https://jsfiddle.net/bmde6L0r/で利用可能な例

于 2016-01-04T14:11:01.450 に答える
0

データを使用してメソッドを転送することは一般的ではないと思いますが、それは素晴らしいアイデアのようです。

このプロジェクトでは、データとともに関数をエンコードできますが、標準とは見なされず、もちろん同じライブラリでデコードする必要があります。

https://github.com/josipk/json-plus
于 2013-02-24T18:01:26.430 に答える