13

Stoyan Stefanov による JavaScript パターンの本を読んでいますが、コンストラクター関数の新しい演算子を強制するパターンの 1 つは次のようになります。

function Waffle() {
if (!(this instanceof Waffle)) {
return new Waffle();
}
this.tastes = "yummy";
}
Waffle.prototype.wantAnother = true;

このように書くと、次のいずれかの方法で Waffle を呼び出すことができます

var first = new Waffle(),
second = Waffle(); 

これは便利な機能だと思いますが、ecma/javascript の将来のバージョンで実装されるかどうかはわかりません

コンストラクター関数を作成するたびにコピーして貼り付けることができると思ったものを自分で思いついた

このようなもの

function checkInstance (name) {
    if (name.constructor.name === undefined) {
       return "construct it"
    } else {
       return false;
    }
}

function Waffle() {
    var _self = checkInstance.call(this, this);
    if (_self === "construct it") {
       return new Waffle()
    }
    this.tastes = "yummy"
}

var waffle = Waffle()
waffle

したがって、 new Waffle または Waffle() のいずれかの方法で Waffle を呼び出しても、オブジェクトを返すことができます。

私が抱えている問題はここにあります

  if (_self === "construct it") {
       return new Waffle()
       }

とにかくnew Waffle()、コンストラクター関数の実際の名前を参照せずに参照できるので、毎回これをコピーして貼り付けることができ、何も変更する必要はありません。つまり、 Waffle() を変数として保存して、次のようなことができます

return new var

this.name を使用できればいいのですが、呼び出されるまで機能しません。

スタックオーバーフローの可能性があるかどうか、少なくともここにいる何人かの人々に尋ねたかったのですが、できません。

繰り返しますが、あなたのコメントやフィードバックは大歓迎です

4

7 に答える 7

8

より良い解決策があります。これはあなたが現在していることです:

function Waffle() {
    if (!(this instanceof Waffle))
        return new Waffle;
    this.tastes = "yummy";
}

Waffle.prototype.wantAnother = true;

newこのパターンは、新しいオブジェクトを構築するためのコードと、キーワードが使用されているかどうかを確認するためのコードを混在させているため、あまり良くありません。

前に、JavaScript でキーワードを使用しないnewでくださいと述べました。これは、機能を壊すためです。代わりに、同じことを行う別の関数を作成しましょう。

Function.prototype.new = (function () {
    return function () {
        functor.prototype = this.prototype;
        return new functor(this, arguments);
    };

    function functor(constructor, args) {
        return constructor.apply(this, args);
    }
}());

この関数を使用すると、次のように関数のインスタンスを作成できます。

var waffle = Waffle.new();

ただし、まったく使用したくありませんnew。そのため、次のようにコンストラクターをラップする関数を作成します。

function constructible(constructor) {
    function functor() { return Function.new.apply(constructor, arguments); }
    functor.prototype = constructor.prototype;
    return functor;
}

Waffleこれで、関数を次のように定義できます。

var Waffle = constructible(function () {
    this.tastes = "yummy";
});

Waffle.prototype.wantAnother = true;

を使用して、または使用せずにオブジェクトを作成できるようになりましたnew

var first = new Waffle;
var second = Waffle();

constructible関数はかなり遅いです。代わりに次のバージョンの を使用してくださいconstructible- 少し高速です:

function constructible(constructor) {
    constructor = Function.bind.bind(constructor, null);
    function functor() { return new (constructor.apply(null, arguments)); }
    functor.prototype = constructor.prototype;
    return functor;
}

個人的には、これら 2 つの方法のいずれも使用しません。を書くことを覚えているnewか、または (より可能性が高い) 次のようにコードを再構築します。

var waffle = {
    create: function () {
        var waffle = Object.create(this);
        waffle.tastes = "yummy";
        return waffle;
    },
    wantAnother: true
};

var first = waffle.create();
var second = waffle.create();

このパターンについて詳しく知りたい場合は、次の回答をお読みください: https://stackoverflow.com/a/17008403/783743

于 2013-06-11T04:03:55.310 に答える
3

arguments.callee現在の関数を参照する は、最も単純なソリューションです。ただし、非推奨なので、自己責任で使用してください。

function Waffle() {
    if (!(this instanceof arguments.callee))
        return new arguments.callee();

    this.tastes = 'yummy';
}

Vinothbabu が述べたように、渡した引数を保持したい可能性が高いため、これも難しい問題です。しかし、本当の意図が enforcingnewである場合は、単にエラーをスローすることができます。これは単純な 2 行のコードです。

if (!(this instanceof Waffle))
    throw new Error('Constructor called without new');

関数でラップすることもできます:

function cons(C) {
    var c = function () {
        if (!(this instanceof c))
            throw new Error('Constructor called without new');

        C.apply(this, arguments);
    };
    c.prototype = C.prototype;
    return c;
}

var Waffle = cons(function () {
    this.tastes = 'yummy';
});
Waffle.prototype.wantAnother = function () {
    return true;
};

new Waffle(); // { tastes: 'yummy', 'wantAnother': true }
Waffle(); // throws error

現在は -- を指定して呼び出すWaffle 必要newがあります。そうしないと、エラーがスローされます。

于 2013-06-10T22:52:34.730 に答える
2

私の意見では、最善のアプローチは、間違って物事を呼び出すことができないようにすることです。

function Waffle() {
  if (!(this instanceof Waffle)) {
    throw "Waffles need to be fresh or they're gross. Use 'new'.";
  }
}

ただし、単に一貫性のないコードを記述できるようにする必要がある場合は、初期化を別の手順にします。

function Waffle(options) {
  var o = options || {};
  if (this instanceof Waffle) {
    this.init = function() {
      /* this is really your constructor */
      console.log("initializing ... ");
    }

    if (!o.__do_not_initialize) {
      this.init(arguments);
    }
  } else {
    var rv = new Waffle( { __do_not_initialize: true } );
    rv.init(arguments);
    return rv;
  }
}

他の方法で一貫性を強制したい場合、つまりnewキーワードを使用しない場合は、ビルダー関数を作成します。

function BuildWaffle(options) {
  var o = options || {};

  if (this instanceof WaffleBuilder) {
    throw "BuildWaffle cannot be instantiated.";
  }

  var Waffle = function Waffle() { /* whatever */ }
  Waffle.prototype.doStuff = function() { /* whatever else */ }

  var rv = new Waffle(options);
  return rv;
}
于 2013-06-11T03:06:47.450 に答える
2

なしでも新しいオブジェクトの作成を強制する簡単な方法がありますnew:

function Waffle() {
    return {tastes:"yummy"};
}

var a = Waffle();
var b = new Waffle();

alert(a.tastes); // yummy
alert(b.tastes); // yummy

説明

newwith 関数を使用すると、次の 2 つの可能性があります。

  • 関数はオブジェクトを返します: オブジェクトはnew function()式の結果です
  • 関数はオブジェクトを返しません: 新しいコンテキストを持つ関数自体が返されます

ECMA スクリプトのドキュメントを参照してください

回避策: プロトタイプと引数

function Waffle(taste,how) {
    return {
        tastes: taste+" "+how,
        __proto__: Waffle.prototype
    }
}
Waffle.prototype.wantmore = "yes";

var a = Waffle("yummy","much");
var b = new Waffle("gummy","little");

console.log(a.tastes,b.tastes); // yummy much, gummy little
console.log(a.wantmore,b.wantmore); // yes, yes

これはフィドルに値します。

注:( constructor.nameパター​​ンで使用したもの)は標準ではありません

注 2: __proto__これも標準ではありませんが、最新のブラウザーでサポートされており、ES6 で標準化される予定です。

于 2013-06-10T23:13:30.117 に答える
1
if (!(this instanceof Waffle)) {
    return new Waffle();
}

これには2つの問題があります...

  1. 名前のない無名関数では機能しないというもの
  2. コンストラクターに送信されたすべての引数が失われます。

より一般的なアプローチを使用すると、次のようになります。

if (!instanceExists(this, arguments)) {
    return requireInstance(this, arguments);
}

このアプローチにより、constructorが で呼び出されるnewことが保証さstate the function' s nameadds all arguments sent to the constuctor so they aren 't lost during the processます。

上記の完全なコードは次のとおりです。

Function.prototype.callNew = function (args) {
    var a = [];
    for (var i = 0; i < args.length; i++) a.push("a[" + i + "]");
    var fn = new Function("var a=arguments;return new this(" + a.join(",") + ");");
    return fn.apply(this, args);
}

function instanceExists(t, args) {
    if (t instanceof args.callee) {
        return true;
    } else {
        return false;
    }
}

function requireInstance(t, args) {
    var fn = args.callee;
    if (!instanceExists(t, args)) {
        return fn.callNew(args);
    }
}

function Waffle(one, two, three) {
    if (!instanceExists(this, arguments)) {
        return requireInstance(this, arguments);
    }
    this.one = one;
    this.two = two;
    this.three = three;
}

Waffle.prototype.serve = function () {
    var out = [];
    for (var j in this) {
        if (!this.hasOwnProperty(j)) continue;
        out.push(j + ': ' + this[j]);
    }
    return ' {
    ' + out.join(",\n") + '
}
';
}

あなたが遊ぶためのフィドル。 http://jsfiddle.net/RkPpH/

var waffle = Waffle(1, 2, 3);
alert(waffle.serve());
于 2013-06-10T22:39:58.633 に答える
0

これがクライアント側なのかサーバー側なのかはわかりませんでしたが、私が時々使用するパターンは次のとおりです。私はこれを Node で使用していますが、クライアント側のソリューションとしても使用できるように試みました。Node 固有のものはコメントアウトされていますが、環境によっては参照用に記載されています。

最初に、従来の OO ベースまたはスーパークラスに沿って使用するものを次のように作成します。

//// Node:
//module.exports.Base = Base;

function Base(opts) {
    var self = this;
    if (!(self instanceof Base)) return new Base(opts);
    self.opts = opts || {};
}

その上で、通常の方法でメソッドを定義できます。抽象のようなものを実装するサブクラスによってメソッドが提供される必要がある場合は、手動でスローすることもできます。

// commonMethod is available to subclasses:
Base.prototype.commonMethod = function () {
    var self = this;
    //access self.opts to get the constructor arguments.
    //makes self always point to the right object.
}

// Provide abstractMethod, but subclass is responsible for implementation:
Base.prototype.abstractMethod = function () {
    //or throw an error if this should be implemented by subclasses:
    throw new Error('implement me');
}

今、あなたはこれを行うことができます:

//// If using Node:
//var inherits = require('util').inherits;
//var Parent = require('./Base').Base;

function Sub (opts) {
    var self = this;
    //// If using node and you  want super_ to be called prior to creating a new Sub:
    //if(Sub.super_) Sub.super_.call(this, opts);

    // Always do this:
    if (!(self instanceof Sub)) return new Sub(opts);

    //// If using node and you are ok with super_ called after creating new Sub:
    //if(Sub.super_) Sub.super_.call(this, opts);
    //// otherwise:
    parent(opts);
}

//// If using Node:
//inherits(Sub, Base);
//// Otherwise:
Sub.prototype.constructor = Base;
Sub.prototype.parent = Base.prototype;

//and provide the implementation of abstractMethod:
Sub.prototype.abstractMethod() {
    //...
}

そして、特定の質問に正式に答えるために、すべての

if (!(self instanceof Sub)) return new Sub(opts);

保証された新しい状況が得られる場所です。

于 2013-06-15T04:31:58.747 に答える