JavaScriptでカスタムオブジェクトを「適切に」作成する方法


471

プロパティとメソッドを持つJavaScriptオブジェクトを作成する最良の方法は何なのかと思います。

スコープが常に正しいことを確認するために、人がすべての関数でvar self = this使用self.し、次に使用する例を見てきました。

次に.prototype、プロパティを追加するためにを使用する例を見ましたが、他の人はインラインでそれを行っています。

誰かがいくつかのプロパティとメソッドを持つJavaScriptオブジェクトの適切な例を教えてもらえますか?


13
「最善の」方法はありません。
トリプティク

self予約語ではありません か?そうでない場合は、そうする必要があります。以降は、self現在のウィンドウを参照して、予め定義された変数です。self === window
Shaz、2011

2
@Shaz:またはのwindowようなブラウザオブジェクトモデルの他のプロパティと同様に、予約語ではありません。識別子を変数名として再利用できます。ええ、そうですが、私は考えられる混乱を避けることを好みます。最終的には無意味ですが、それに触れる理由はほとんどありません。documentframesvar that= thiswindow.self
ボビンス、

7
JSが縮小されている場合this、ローカル変数(などself)に割り当てると、ファイルサイズが小さくなります。
Patrick Fisher

Classjsの新しいリンク:github.com/divio/classjs
Nikola

回答:


889

JavaScriptでクラスとインスタンスを実装するための2つのモデルがあります。プロトタイピングの方法とクロージャーの方法です。どちらにも長所と短所があり、さまざまな拡張バリエーションがあります。多くのプログラマーとライブラリーには、言語の醜い部分のいくつかを説明するためのさまざまなアプローチとクラス処理ユーティリティ関数があります。

その結果、混合企業では、メタクラスのミッシュマッシュがあり、すべて動作が少し異なります。さらに悪いことに、ほとんどのJavaScriptチュートリアル資料はひどいものであり、あらゆる中間点をカバーするためにある種の中間的な妥協点を提供しており、非常に混乱しています。(おそらく、作者も混乱しています。JavaScriptのオブジェクトモデルは、ほとんどのプログラミング言語とは非常に異なり、多くの場所でまっすぐに設計が不適切です。)

プロトタイプの方法から始めましょう。これは、取得できる最もJavaScriptネイティブです。最小限のオーバーヘッドコードがあり、instanceofはこの種類のオブジェクトのインスタンスで動作します。

function Shape(x, y) {
    this.x= x;
    this.y= y;
}

このコンストラクター関数のルックアップにメソッドをnew Shape書き込むことで、作成されたインスタンスにメソッドを追加できprototypeます。

Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

次に、JavaScriptがサブクラス化することを呼び出すことができる限り、それをサブクラス化します。私たちはその奇妙な魔法のprototypeプロパティを完全に置き換えることによってそれを行います:

function Circle(x, y, r) {
    Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
    this.r= r;
}
Circle.prototype= new Shape();

メソッドを追加する前に:

Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

この例は機能し、多くのチュートリアルでこのようなコードが表示されます。しかし、それnew Shape()は醜いです。実際のShapeを作成する必要はありませんが、基本クラスをインスタンス化しています。JavaScriptはとてもずさんであるので、この単純なケースで動作するように起こる:それはゼロ引数が、その場合には、渡すことを可能にするxyなるundefinedと、プロトタイプのに割り当てられているthis.xthis.y。コンストラクター関数がより複雑なことを行っている場合、その関数は表面上で平坦になります。

したがって、基本クラスのコンストラクター関数を呼び出さずに、クラスレベルで必要なメソッドとその他のメンバーを含むプロトタイプオブジェクトを作成する方法を見つける必要があります。これを行うには、ヘルパーコードの作成を開始する必要があります。これは私が知っている最も簡単なアプローチです:

function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

これにより、プロトタイプ内の基本クラスのメンバーが、何もしない新しいコンストラクター関数に転送され、そのコンストラクターが使用されます。これで簡単に書くことができます:

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

new Shape()間違いの代わりに。これで、ビルドされたクラスに受け入れ可能なプリミティブのセットができました。

このモデルで検討できる改良点と拡張機能がいくつかあります。たとえば、構文糖バージョンは次のとおりです。

Function.prototype.subclass= function(base) {
    var c= Function.prototype.subclass.nonconstructor;
    c.prototype= base.prototype;
    this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};

...

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.subclass(Shape);

どちらのバージョンにも、コンストラクター関数を継承できないという欠点があります。これは、多くの言語にあるためです。したがって、サブクラスが構築プロセスに何も追加しない場合でも、ベースが必要な引数を使用してベースコンストラクターを呼び出すことを忘れないでください。これはを使用してわずかに自動化できますapplyが、それでも書き出す必要があります。

function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

したがって、一般的な拡張は、コンストラクタ自体ではなく、初期化機能を独自の関数に分割することです。この関数は、ベースから問題なく継承できます。

function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!

これで、各クラスに同じコンストラクタ関数のボイラープレートができました。たぶん、それを独自のヘルパー関数に移動できるので、入力を続ける必要がなくなります。たとえばFunction.prototype.subclass、それを回転させて、基本クラスの関数にサブクラスを吐かせます。

Function.prototype.makeSubclass= function() {
    function Class() {
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
    Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

...

Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

Point= Shape.makeSubclass();

Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
    Shape.prototype._init.call(this, x, y);
    this.r= r;
};

...構文は少し不格好ですが、他の言語に少し似ています。必要に応じて、いくつかの機能を追加できます。たぶん、あなたmakeSubclassはクラス名を取って覚えて、toStringそれを使ってデフォルトを提供したいでしょう。おそらく、コンストラクターがnew演算子なしで誤って呼び出された場合にコンストラクターを検出するようにしたい場合があります(そうしないと、デバッグが非常に煩わしいことがよくあります)。

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without "new"');
        ...

新しいメンバーをすべて渡しmakeSubclassてプロトタイプに追加すると、Class.prototype...多くのことを書く必要がなくなります。多くのクラスシステムはそれを行います、例えば:

Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

オブジェクトシステムには望ましいと思われる多くの潜在的な機能があり、特定の数式に誰も同意しません。


閉鎖方法、そして。これにより、継承をまったく使用しないことで、JavaScriptのプロトタイプベースの継承の問題を回避します。代わりに:

function Shape(x, y) {
    var that= this;

    this.x= x;
    this.y= y;

    this.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };
}

function Circle(x, y, r) {
    var that= this;

    Shape.call(this, x, y);
    this.r= r;

    var _baseToString= this.toString;
    this.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+that.r;
    };
};

var mycircle= new Circle();

これで、のすべてのインスタンスにShapetoStringメソッド(およびその他のメソッドまたは追加したその他のクラスメンバー)の独自のコピーがあります。

各クラスメンバーの独自のコピーを持つすべてのインスタンスの悪い点は、効率が悪いことです。多数のサブクラス化されたインスタンスを処理している場合は、プロトタイプの継承が適している場合があります。また、ご覧のとおり、基本クラスのメソッドを呼び出すのは少し面倒です。サブクラスコンストラクターがメソッドを上書きする前に、メソッドが何であったかを覚えておかないと、失われます。

[ここにも継承がないため、instanceof演算子は機能しません。必要な場合は、クラスを探知するための独自のメカニズムを提供する必要があります。プロトタイプ継承と同様の方法でプロトタイプオブジェクトをいじることはできます、少しトリッキーであり、実際に機能させるだけの価値はありませんinstanceof。]

独自のメソッドを持つすべてのインスタンスの良い点は、メソッドがそれを所有する特定のインスタンスにバインドされることです。これはthis、メソッド呼び出しでのJavaScriptの奇妙なバインディング方法のために役立ちます。これは、メソッドをその所有者から切り離すと、次のようになります。

var ts= mycircle.toString;
alert(ts());

その場合this、メソッドの内部は予想どおりCircleインスタンスにはなりません(実際にはグローバルwindowオブジェクトになるため、広範囲にわたるデバッグの問題が発生します)。実際には、これは通常、メソッドが取得され、に割り当てられたsetTimeout場合、onclickまたはEventListener一般的に発生します。

プロトタイプの方法では、そのような割り当てごとにクロージャーを含める必要があります。

setTimeout(function() {
    mycircle.move(1, 1);
}, 1000);

または、将来的に(または、Function.prototypeをハッキングする場合は)、次のように実行することもできますfunction.bind()

setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

インスタンスがクロージャーの方法で行われる場合、バインディングはインスタンス変数のクロージャーによって無料で行われます(通常、thatまたはと呼ばれselfますが、個人的にはself、JavaScriptですでに別の異なる意味を持っているので、後者に対してアドバイスします)。1, 1ただし、上記のスニペットの引数を無料で取得することはできないため、別のクロージャーbind()が必要になるか、それを行う必要がある場合は、

クロージャーメソッドにも多くのバリアントがあります。演算子を使用する代わりにthis、完全に省略して、新規に作成してthat返すことをお勧めしnewます。

function Shape(x, y) {
    var that= {};

    that.x= x;
    that.y= y;

    that.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };

    return that;
}

function Circle(x, y, r) {
    var that= Shape(x, y);

    that.r= r;

    var _baseToString= that.toString;
    that.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+r;
    };

    return that;
};

var mycircle= Circle(); // you can include `new` if you want but it won't do anything

どの方法が「適切」ですか?どちらも。「最高」はどれですか?それはあなたの状況に依存します。FWIW私は、OOオブジェクトを強く実行している場合は、実際のJavaScript継承のプロトタイピングを行い、単純な使い捨てページ効果のクロージャーを作成する傾向があります。

しかし、どちらの方法も、ほとんどのプログラマにとって直感に反しています。どちらも多くの乱雑な潜在的なバリエーションがあります。他の人のコード/ライブラリーを使用する場合、両方(および多くの中間および一般的に壊れたスキーム)に適合します。一般的に受け入れられている答えはありません。JavaScriptオブジェクトのすばらしい世界へようこそ。

[これは、JavaScriptが私のお気に入りのプログラミング言語ではない理由の94部です。]


13
「クラス」のdefからオブジェクトのインスタンス化への段階的なステップスルー。そして、バイパスのいい感じnew
クレセントフレッシュ

8
JavaScriptはクラスがあるかのように使用したいので、好きな言語ではないようです。
ジョナサンファインバーグ、

59
もちろん、私もそうです。クラスとインスタンスのモデルは、プログラマーが今日直面している一般的な問題の大半にとって、より自然なものです。理論的には、プロトタイプベースの継承がより柔軟な作業方法を提供できる可能性があることには同意しますが、JavaScriptは完全にその約束を果たしていません。その不格好なコンストラクター関数システムは、両方の世界で最悪の状況をもたらし、プロトタイプが提供できる柔軟性または単純さを提供しない一方で、クラスのような継承を困難にします。つまり、うんちです。
ボビンス、2009年

4
ボブこれは素晴らしい答えだと思います。私はしばらくの間、これら2つのパターンに取り組んできましたが、Resigよりも簡潔にコーディングし、Crockfordよりも洞察力をもって説明したと思います。私は考えることができるん高い賞賛ん....
ジェームズウエストゲート

4
古典的な継承パラダイムをjavascriptのようなプロトタイプ言語にグラフ化することは、四角いペグであり、丸い穴であるように私には常に思えました。これが本当に必要なときがあるのでしょうか、それとも、単にその言語をその目的に使用するのではなく、人々が自分のやりたいようにその言語を模造する方法にすぎないのでしょうか。
slf '10

90

私はこのパターンをかなり頻繁に使用しています。必要なときに、非常に大きな柔軟性が得られることがわかりました。使用中は、Javaスタイルのクラスにかなり似ています。

var Foo = function()
{

    var privateStaticMethod = function() {};
    var privateStaticVariable = "foo";

    var constructor = function Foo(foo, bar)
    {
        var privateMethod = function() {};
        this.publicMethod = function() {};
    };

    constructor.publicStaticMethod = function() {};

    return constructor;
}();

これは、作成時に呼び出される無名関数を使用して、新しいコンストラクター関数を返します。無名関数は1回しか呼び出されないため、その中にプライベート静的変数を作成できます(これらはクロージャーの内側にあり、クラスの他のメンバーから見ることができます)。コンストラクター関数は基本的に標準のJavaScriptオブジェクトです。その内部にプライベート属性を定義し、パブリック属性をthis変数に付加します。

基本的に、このアプローチは、クロックフォードのアプローチと標準のJavaScriptオブジェクトを組み合わせて、より強力なクラスを作成します。

他のJavaScriptオブジェクトと同じように使用できます。

Foo.publicStaticMethod(); //calling a static method
var test = new Foo();     //instantiation
test.publicMethod();      //calling a method

4
これは、C#である "home-turf"にかなり近いので、面白そうです。私はまた、privateStaticVariableが本当にプライベートである理由を理解し始めたと思います(関数のスコープ内で定義され、それへの参照がある限り存続します?)
Michael Stum

使用thisしていないので、それでもインスタンス化する必要がありますnewか?
ジョーダンパーマー、

実際にthis 、例のようにconstructorなる関数で使用されFooます。
ShZ

4
ここでの問題は、すべてのオブジェクトがすべてのプライベート関数とパブリック関数の独自のコピーを取得することです。
virtualnobi 2013

2
@virtualnobi:このパターンはprotytpeメソッドの作成を妨げません:constructor.prototype.myMethod = function () { ... }
Nicolas Le Thierry d'Ennequin 2014

25

Douglas Crockfordは、そのトピックについてThe Good Partsで詳しく説明しています。彼は、新しいオブジェクトを作成するために新しいオペレーターを避けることをお勧めします。代わりに、彼はカスタマイズされたコンストラクタを作成することを提案しています。例えば:

var mammal = function (spec) {     
   var that = {}; 
   that.get_name = function (  ) { 
      return spec.name; 
   }; 
   that.says = function (  ) { 
      return spec.saying || ''; 
   }; 
   return that; 
}; 

var myMammal = mammal({name: 'Herb'});

JavaScriptでは、関数はオブジェクトであり、新しい演算子とともにオブジェクトを構築するために使用できます。慣例により、コンストラクタとして使用することを目的とした関数は大文字で始まります。あなたはしばしば次のようなものを見ます:

function Person() {
   this.name = "John";
   return this;
}

var person = new Person();
alert("name: " + person.name);**

新しいオブジェクトをインスタンス化するときにnew演算子を使用し忘れた場合、通常の関数呼び出しが行われ、これは新しいオブジェクトではなくグローバルオブジェクトにバインドされます。


5
それが私なのか、それとも彼が新しいオペレーターを打ち明けても、クロックフォードはまったく意味がないと思いますか?
meder omuraliev 09/10/20

3
@meder:あなただけではありません。少なくとも、私は新しいオペレーターには何の問題もないと思います。とにかく暗黙のうちnewにありvar that = {};ます。
ティムダウン

17
Crockfordは不機嫌そうな老人であり、私は彼に多くの点で反対していますが、彼は少なくともJavaScriptを批判的に検討することを推進しており、彼の意見を聞く価値はあります。
ボビンス2009年

2
@bobince:そうですね。彼がクロージャーについて書いたことは、約5年前に多くのことに目を向け、思慮深いアプローチを奨励しています。
ティムダウン、

20
私はクロックフォードに同意します。新しい演算子の問題は、JavaScriptが「this」のコンテキストを、他の方法で関数を呼び出す場合と大きく異なることです。適切な大文字小文字の規約にかかわらず、開発者が新しいの使用を忘れたり、大文字の使用を忘れたりするなど、より大きなコードベースで問題が発生します。実用的にするために、新しいキーワードなしで必要なすべてのことを実行できます。コードにさらに多くの障害点を導入していますか?JSは、クラスベースではなく、プロトタイプの言語です。では、なぜ静的型付け言語のように振る舞わせたいのでしょうか?私は確かにしません。
ジョシュアラミレス

13

ボビンスの答えを続けて

es6では、実際に class

だから今できること:

class Shape {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Shape at ${this.x}, ${this.y}`;
    }
}

したがって、(他の答えのように)円に拡張すると、次のことができます:

class Circle extends Shape {
    constructor(x, y, r) {
        super(x, y);
        this.r = r;
    }

    toString() {
        let shapeString = super.toString();
        return `Circular ${shapeString} with radius ${this.r}`;
    }
}

es6では少しすっきりとして読みやすくなりました。


これが実際の良い例です:


6

構造を使用して、次のように行うこともできます。

function createCounter () {
    var count = 0;

    return {
        increaseBy: function(nb) {
            count += nb;
        },
        reset: function {
            count = 0;
        }
    }
}

次に:

var counter1 = createCounter();
counter1.increaseBy(4);

6
空白が重要であるため、私はその方法が好きではありません。復帰後のカーリーは、ブラウザー間の互換性のために同じ行にある必要があります。
geowa4 2009年

5

別の方法は、http://jsfiddle.net/nnUY4/です (この種のオブジェクト作成処理と公開関数が特定のパターンに従っているかどうかはわかりません)。

// Build-Reveal

var person={
create:function(_name){ // 'constructor'
                        //  prevents direct instantiation 
                        //  but no inheritance
    return (function() {

        var name=_name||"defaultname";  // private variable

        // [some private functions]

        function getName(){
            return name;
        }

        function setName(_name){
            name=_name;
        }

        return {    // revealed functions
            getName:getName,    
            setName:setName
        }
    })();
   }
  }

  // … no (instantiated) person so far …

  var p=person.create(); // name will be set to 'defaultname'
  p.setName("adam");        // and overwritten
  var p2=person.create("eva"); // or provide 'constructor parameters'
  alert(p.getName()+":"+p2.getName()); // alerts "adam:eva"

4

コンストラクターの呼び出し中に「this」を閉じるトリックを使用する場合、オブジェクトのメソッドを呼び出さない他のオブジェクトがコールバックとして使用できる関数を作成するためです。「スコープを正しくする」こととは関係ありません。

これが基本的なJavaScriptオブジェクトです。

function MyThing(aParam) {
    var myPrivateVariable = "squizzitch";

    this.someProperty = aParam;
    this.useMeAsACallback = function() {
        console.log("Look, I have access to " + myPrivateVariable + "!");
    }
}

// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
    console.log(this.someProperty);
};

JavaScriptについてDouglas Crockfordが言っていることを読むと、たくさんのことを学べるでしょう。ジョン・レジグも素晴らしい。幸運を!


1
ええと、周りthisを閉じることは、「スコープを正しくする」ことと関係があります。
Roatin Marth

3
ジョナサンは正しいです。js関数のスコープは、あなたが設計したものです。self = thisトリックは、特定のインスタンスに関連付けるための1つの方法であるため、別のコンテキストで呼び出されても変更されません。しかし、時にはそれが実際に必要なことです。コンテキストによって異なります。
Marco、

あなたは皆同じことを言っていると思います。持続するself=thisことを強制thisするわけではありませんが、クロージャーを介して「正しい」スコープを簡単に許可します。
クレセントフレッシュ

2
that = thisを行う理由は、ネストされた関数がコンストラクタ関数に存在するthisのスコープにアクセスできるようにするためです。ネストされた関数がコンストラクター関数内にある場合、それらの「this」スコープはグローバルスコープに戻ります。
ジョシュアラミレス

4

Closure汎用性があります。bobinceは、オブジェクトを作成する際のプロトタイプとクロージャーのアプローチをよくまとめています。ただしOOP、関数型プログラミングでクロージャーを使用するいくつかの側面を模倣できます。関数はJavaScriptのオブジェクトであることを思い出してください。したがって、別の方法で関数をオブジェクトとして使用します。

閉鎖の例を次に示します。

function outer(outerArg) {
    return inner(innerArg) {
        return innerArg + outerArg; //the scope chain is composed of innerArg and outerArg from the outer context 
    }
}

少し前に、クロージャに関するMozillaの記事を見つけました。これが私の目をジャンプさせます。「クロージャは、データ(環境)をそのデータを操作する関数に関連付けることができます。これは、オブジェクト指向プログラミングに類似しており、オブジェクトがデータ(オブジェクトのプロパティ)を関連付けることができます。 )1つ以上のメソッドを使用します。プロトタイプを参照せずに、クロージャーとクラシックOOPの類似性を読んだのはこれが初めてでした。

どうやって?

一部のアイテムのVATを計算するとします。VATは、アプリケーションの存続期間中、安定している可能性があります。OOP(疑似コード)でそれを行う1つの方法:

public class Calculator {
    public property VAT { get; private set; }
    public Calculator(int vat) {
        this.VAT = vat;
    }
    public int Calculate(int price) {
        return price * this.VAT;
    }
}

基本的に、VAT値をコンストラクターに渡し、計算メソッドはクロージャーを介してその値を操作できます。クラス/コンストラクタを使用する代わりに、VATを引数として関数に渡します。興味があるのは計算そのものだけなので、計算メソッドである新しい関数を返します。

function calculator(vat) {
    return function(item) {
        return item * vat;
    }
}
var calculate = calculator(1.10);
var jsBook = 100; //100$
calculate(jsBook); //110

プロジェクトで、計算対象のVATの候補となるトップレベルの値を特定します。経験則として、同じ引数を何度も渡すときは常に、クロージャーを使用して改善する方法があります。従来のオブジェクトを作成する必要はありません。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures


3

オブジェクトを作成する

JavaScriptでオブジェクトを作成する最も簡単な方法は、次の構文を使用することです。

var test = {
  a : 5,
  b : 10,
  f : function(c) {
    return this.a + this.b + c;
  }
}

console.log(test);
console.log(test.f(3));

これは、構造化された方法でデータを格納するのに最適です。

ただし、より複雑なユースケースでは、関数のインスタンスを作成する方がよい場合がよくあります。

function Test(a, b) {
  this.a = a;
  this.b = b;
  this.f = function(c) {
return this.a + this.b + c;
  };
}

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

これにより、たとえばのクラスの使用方法と同様に、同じ「青写真」を共有する複数のオブジェクトを作成できます。Java。

ただし、プロトタイプを使用することで、より効率的に行うことができます。

関数の異なるインスタンスが同じメソッドまたはプロパティを共有するときはいつでも、それらをそのオブジェクトのプロトタイプに移動できます。このように、関数のすべてのインスタンスはそのメソッドまたはプロパティにアクセスできますが、すべてのインスタンスで複製する必要はありません。

私たちの場合、メソッドfをプロトタイプに移動することは理にかなっています:

function Test(a, b) {
  this.a = a;
  this.b = b;
}

Test.prototype.f = function(c) {
  return this.a + this.b + c;
};

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

継承

JavaScriptで継承を行う簡単で効果的な方法は、次の2行を使用することです。

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

これはこれを行うのと似ています:

B.prototype = new A();

両方の主な違いは、をA使用する場合、のコンストラクターが実行されないObject.createことです。これは、より直感的で、クラスベースの継承に似ています。

のコンストラクタにAインスタンスをB追加して、の新しいインスタンスを作成するときに、いつでもオプションでコンストラクタを実行するように選択できますB

function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

あなたは、すべての引数を渡したい場合BにはA、あなたも使用することができますFunction.prototype.apply()

function B() {
    A.apply(this, arguments); // This is optional
}

あなたがのコンストラクタチェーンに別のオブジェクトをミックスインする場合B、あなたは組み合わせることができますObject.createObject.assign

B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

デモ

function A(name) {
  this.name = name;
}

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());


注意

Object.createIE9 +を含むすべての最新ブラウザーで安全に使用できます。Object.assignIEの一部のバージョンや一部のモバイルブラウザーでは機能しません。ポリフィルすること Object.create、および/またはObject.assignそれらを使用し、それらを実装していないブラウザをサポートすることをお勧めします。

Object.create ここここにポリフィルがありObject.assign ます


0
var Person = function (lastname, age, job){
this.name = name;
this.age = age;
this.job = job;
this.changeName = function(name){
this.lastname = name;
}
}
var myWorker = new Person('Adeola', 23, 'Web Developer');
myWorker.changeName('Timmy');

console.log("New Worker" + myWorker.lastname);

4
それは、すでに提供されている多数の広範な回答に何を追加しますか?
blm 2015年

簡潔で、実装の3つの部分を示しているので、私はこの回答が好きです。1)オブジェクトを定義します。2)オブジェクトのインスタンスをインスタンス化します。3)インスタンスを使用します。解析するのではなく、一目ですべてを示します。上記のすべての詳細な回答(もちろん、すべての関連する詳細が必要な非常に優れた回答です)-簡単な要約の一部
G-Man

0

2009年に承認された回答に加えて、最新のブラウザーをターゲットにできる場合は、Object.definePropertyを利用できます。

Object.defineProperty()メソッドは、オブジェクトの新しいプロパティを直接定義するか、オブジェクトの既存のプロパティを変更して、オブジェクトを返します。出典:Mozilla

var Foo = (function () {
    function Foo() {
        this._bar = false;
    }
    Object.defineProperty(Foo.prototype, "bar", {
        get: function () {
            return this._bar;
        },
        set: function (theBar) {
            this._bar = theBar;
        },
        enumerable: true,
        configurable: true
    });
    Foo.prototype.toTest = function () {
        alert("my value is " + this.bar);
    };
    return Foo;
}());

// test instance
var test = new Foo();
test.bar = true;
test.toTest();

デスクトップおよびモバイルの互換性リストを確認するには、Mozillaのブラウザ互換性リストを参照してください。はい、IE9 +はSafariモバイルと同様にそれをサポートしています。


0

これも試すことができます

    function Person(obj) {
    'use strict';
    if (typeof obj === "undefined") {
        this.name = "Bob";
        this.age = 32;
        this.company = "Facebook";
    } else {
        this.name = obj.name;
        this.age = obj.age;
        this.company = obj.company;
    }

}

Person.prototype.print = function () {
    'use strict';
    console.log("Name: " + this.name + " Age : " + this.age + " Company : " + this.company);
};

var p1 = new Person({name: "Alex", age: 23, company: "Google"});
p1.print();

0
うまく機能するパターン
var Klass = function Klass() {
    var thus = this;
    var somePublicVariable = x
      , somePublicVariable2 = x
      ;
    var somePrivateVariable = x
      , somePrivateVariable2 = x
      ;

    var privateMethod = (function p() {...}).bind(this);

    function publicMethod() {...}

    // export precepts
    this.var1 = somePublicVariable;
    this.method = publicMethod;

    return this;
};

まず、コンストラクタのprototypeオブジェクトではなく、インスタンスにメソッドを追加する設定を変更できます。継承とデコレータに関する目的でコンストラクタハイジャックを頻繁に使用するため、ほとんどの場合、コンストラクタ内でメソッドを宣言します。

ここで、どの宣言が書かれるかを決定する方法を示します。

  • コンテキストオブジェクトで直接メソッドを宣言しないでください(this
  • してみましょうvar宣言が優先しますfunction宣言
  • プリミティブをオブジェクトよりも優先させる({}および[]
  • してみましょうpublic宣言が優先しますprivate宣言
  • 好むFunction.prototype.bind以上thusselfvmetc
  • 次の場合を除き、別のクラス内でクラスを宣言しないでください。
    • 2つは不可分であることは明らかです
    • Innerクラスはコマンドパターンを実装します
    • Innerクラスはシングルトンパターンを実装します
    • 内部クラスは状態パターンを実装します
    • 内部クラスは、これを保証する別のデザインパターンを実装します
  • クロージャースペースの字句範囲this内から常に戻ります。

これらが役立つ理由は次のとおりです。

コンストラクターのハイジャック
var Super = function Super() {
    ...
    this.inherited = true;
    ...
};
var Klass = function Klass() {
    ...
    // export precepts
    Super.apply(this);  // extends this with property `inherited`
    ...
};
モデルデザイン
var Model = function Model(options) {
    var options = options || {};

    this.id = options.id || this.id || -1;
    this.string = options.string || this.string || "";
    // ...

    return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: 'modified' });
(model === updated === true);  // > true
デザインパターン
var Singleton = new (function Singleton() {
    var INSTANCE = null;

    return function Klass() {
        ...
        // export precepts
        ...

        if (!INSTANCE) INSTANCE = this;
        return INSTANCE;
    };
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true);  // > true

あなたが見ることができるように、私は本当には必要ありませんthus、私は以来、好むFunction.prototype.bind(または.callまたは.applyオーバー)thus。私たちのSingletonクラスでは、より多くの情報を伝えるthusため、名前を付けませんINSTANCE。についてはModel、をthis使用.callしてコンストラクターを呼び出し、渡されたインスタンスを返すために使用します。冗長に、変数に割り当てましたが、updated他のシナリオでも役立ちます。

それに加えて、new{brackets}よりもキーワードを使用してオブジェクトリテラルを構築することを好みます。

優先
var klass = new (function Klass(Base) {
    ...
    // export precepts
    Base.apply(this);  //
    this.override = x;
    ...
})(Super);
好まない
var klass = Super.apply({
    override: x
});

ご覧のとおり、後者にはスーパークラスの「オーバーライド」プロパティをオーバーライドする機能がありません。

クラスのprototypeオブジェクトにメソッドを追加する場合は、newキーワードを使用するかどうかにかかわらず、オブジェクトリテラルを好みます。

優先
Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
    ...
    // export precepts
    Base.apply(this);
    ...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
    method: function m() {...}
};
好まない
Klass.prototype.method = function m() {...};

0

タイトルまたは文字列を使用してオブジェクトを宣言できることを述べておきます。
それらの各タイプを呼び出す方法はいくつかあります。下記参照:

var test = {

  useTitle : "Here we use 'a Title' to declare an Object",
  'useString': "Here we use 'a String' to declare an Object",
  
  onTitle : function() {
    return this.useTitle;
  },
  
  onString : function(type) {
    return this[type];
  }
  
}

console.log(test.onTitle());
console.log(test.onString('useString'));


-1

基本的に、JSにはクラスの概念がないため、既存の設計パターンに関連するクラスコンストラクターとして関数を使用します。

//Constructor Pattern
function Person(name, age, job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.doSomething = function(){
    alert('I am Happy');
}
}

今まで、JSにはオブジェクトを作成する手がかりがないため、ここに新しいキーワードがあります。

var person1 = new Person('Arv', 30, 'Software');
person1.name //Arv

参照:Web開発者向けのプロフェッショナルJS-Nik Z


反対票が受け入れられた:正当な理由があれば、より有益であり、改善の機会を提供していたでしょう。
Airwind711 2015

そこの考え方classは、使用して、あなたの見出しに述べたようにJSでは、functionキーワードを。それはだないデザインパターンが、言語の意図的な機能です。私はこれについてあなたに反対票を投じませんでしたが、簡潔さと質問にほとんど関係がないため、他の誰かが投票したようです。このフィードバックがお役に立てば幸いです。
コーディ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.