JavaScriptでの動的オブジェクト構築?

dynamic-languages javascript
JavaScriptでの動的オブジェクト構築?

他の場所から提供された引数を使用してjavascriptで関数を呼び出したい場合、関数の `apply`メソッドを次のように使用できます。

array = ["arg1", 5, "arg3"]
...
someFunc.apply(null, array);

しかし、同様の方法でコンストラクターを呼び出す必要がある場合はどうなりますか? これはうまくいかないようです。

array = ["arg1", 5, "arg3"]
...
someConstructor.apply({}, array);

少なくとも私が試みているようには:

template = ['string1', string2, 'etc'];
var resultTpl = Ext.XTemplate.apply({}, template);

これは以下では機能しません:

Ext.XTemplate.prototype.constructor.apply({}, template);

それを機能させる方法はありますか? (この特定のケースでは、「新しいExt.XTemplate(template)」が機能することがわかりましたが、一般的なケースに興味があります)

同様の質問ですが、組み込みタイプに固有であり、回答なしで使用できます:https://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototypeconstructorapply [プロトタイプを呼び出してJavaScriptオブジェクトをインスタンス化する.constructor.apply]

ありがとうございました。

編集する

時間が経ち、ES6とトランスパイラーは今やものです。 ES6では、 `new someConstructor(…​ array)`でしたかったのは簡単です。 http://babeljs.io [Babel]はそれをES5に変換します。`new(Function.prototype.bind.apply(someConstructor、[null] .concat(array)))(); `https://で説明されていますstackoverflow.com/questions/4226646/how-to-construct-javascript-object-using-apply/4226801#4226801[JavaScriptオブジェクトを構築する方法( ‘apply’を使用)?]。

  16  7


ベストアンサー

コンストラクター関数を使用してこれを行うための単純で簡単な方法はありません。 これは、 `new`キーワードを使用してコンストラクター関数を呼び出すと特別なことが起こるため、そうしない場合は、これらの特別なすべてをエミュレートする必要があるためです。 彼らです:

  1. 新しいオブジェクトインスタンスを作成します(それを実行しています)。

  2. そのオブジェクトの内部プロトタイプをコンストラクター関数に設定する
    `プロトタイプ`プロパティ。

  3. そのオブジェクトの `constructor`プロパティを設定します。

  4. そのオブジェクトインスタンスを使用してコンストラクター関数を呼び出す
    `this`の値(あなたはそれをしている)。

  5. コンストラクター関数からの特別な戻り値の処理。

私はそれについてですが、http://www.ecma-international.org/publications/standards/Ecma-262.htm [仕様書]で再確認する価値があります。

それを避けて、コンストラクター関数を直接使用することができれば、私はそうします。 :-)しかし、それができない場合でも、それを行うことができます、それは単に厄介であり、回避策を伴います。 (https://stackoverflow.com/questions/3853978/are-these-two-ways-of-constructing-an-object-in-javascript-equivalent/3853998#3853998 [この関連する回答] StackOverflowも参照してください私はここ[そして次にいくつか]のすべての地面をカバーします。)

最大の問題は、上記の#2です。オブジェクトの内部プロトタイプを設定します。 長い間、これを行う標準的な方法はありませんでした。 一部のブラウザは、それを行う proto`プロパティをサポートしているため、存在する場合はそれを使用できます。 良いニュースは、ECMAScript 5がこれを明示的に行う方法を導入していることです: `Object.create。 したがって、Chromeのような最先端のブラウザにはそれがあります。 しかし、 Object.create`と proto`のどちらも持たないブラウザを扱っている場合、少し見苦しくなります。

1)カスタムコンストラクター関数を定義します。

2) prototype`プロパティを実際のコンストラクタ関数の prototype`プロパティに設定します

3)空のオブジェクトインスタンスを作成するために使用します。

これがプロトタイプを処理します。 その後、次の手順に進みます。

4)そのインスタンスの `constructor`プロパティを実際のコンストラクター関数に置き換えます。

5) `apply`を介して実際のコンストラクター関数を呼び出します。

6)実際のコンストラクター関数の戻り値がオブジェクトの場合、作成したものの代わりにそれを使用します。それ以外の場合は、作成したものを使用します。

このようなもの(http://jsbin.com/ibusi3/3[live example]):

function applyConstruct(ctor, params) {
    var obj, newobj;

    // Use a fake constructor function with the target constructor's
    // `prototype` property to create the object with the right prototype
    function fakeCtor() {
    }
    fakeCtor.prototype = ctor.prototype;
    obj = new fakeCtor();

    // Set the object's `constructor`
    obj.constructor = ctor;

    // Call the constructor function
    newobj = ctor.apply(obj, params);

    // Use the returned object if there is one.
    // Note that we handle the funky edge case of the `Function` constructor,
    // thanks to Mike's comment below. Double-checked the spec, that should be
    // the lot.
    if (newobj !== null
        && (typeof newobj === "object" || typeof newobj === "function")
       ) {
        obj = newobj;
    }

    // Done
    return obj;
}

さらに一歩進んで、必要な場合にのみ偽のコンストラクタを使用し、次のように Object.create`または proto`が最初にサポートされているかどうかを確認できます(http://jsbin.com/ibusi3/4[live example ]):

function applyConstruct(ctor, params) {
    var obj, newobj;

    // Create the object with the desired prototype
    if (typeof Object.create === "function") {
        // ECMAScript 5
        obj = Object.create(ctor.prototype);
    }
    else if ({}.__proto__) {
        // Non-standard __proto__, supported by some browsers
        obj = {};
        obj.__proto__ = ctor.prototype;
        if (obj.__proto__ !== ctor.prototype) {
            // Setting it didn't work
            obj = makeObjectWithFakeCtor();
        }
    }
    else {
        // Fallback
        obj = makeObjectWithFakeCtor();
    }

    // Set the object's constructor
    obj.constructor = ctor;

    // Apply the constructor function
    newobj = ctor.apply(obj, params);

    // If a constructor function returns an object, that
    // becomes the return value of `new`, so we handle
    // that here.
    if (typeof newobj === "object") {
        obj = newobj;
    }

    // Done!
    return obj;

    // Subroutine for building objects with specific prototypes
    function makeObjectWithFakeCtor() {
        function fakeCtor() {
        }
        fakeCtor.prototype = ctor.prototype;
        return new fakeCtor();
    }
}

Chrome 6では、上記は `Object.create`を使用します。 Firefox 3.6およびOperaでは、 `proto`を使用します。 IE8では、偽のコンストラクター関数を使用します。

上記はかなり簡単ですが、この領域で私が知っている問題のほとんどを処理します。

34


developer.mozillaより:バインドされた関数は、作成された新しいインスタンスを構築するためにnew演算子での使用に自動的に適していますターゲット関数によって。 バインドされた関数を使用して値を構築する場合、これは無視されます。 ただし、提供された引数はコンストラクター呼び出しの前に追加されます。

とはいえ、配列から引数を取得してバインド呼び出しに適用するには、まだapplyを使用する必要があります。 さらに、適用関数のthis引数としてバインドの関数もリセットする必要があります。 これにより、必要に応じて正確に機能する非常に簡潔なワンライナーが提供されます。

function constructorApply(ctor, args){
    return new (ctor.bind.apply(ctor, [null].concat(args)))();
};

3


元の質問はかなり古いので、ここに、事実上すべての最新のブラウザーでのより簡単な方法があります(2018年の執筆時点で、Chrome、FF、IE11、Edgeを数えています…​)。

var template = ['string1', string2, 'etc'];
var resultTpl = Object.create(Ext.XTemplate.prototype);
Ext.XTemplate.apply(resultTpl, template);

これらの2行は、「new」演算子が基本的にどのように機能するかも説明しています。

1


これを達成する別の方法は、Ext Templateクラスを拡張して、新しいオブジェクトのコンストラクターが配列を受け取り、やりたいことを行うようにすることだと思います。 このようにして、すべての構築作業が自動的に行われ、スーパークラスのコンストラクターを引数で呼び出すことができます。

0


タイトルとURLをコピーしました