オブジェクトリテラル/初期化子の自己参照


705

次のようなものをJavaScriptで動作させる方法はありますか?

var foo = {
    a: 5,
    b: 6,
    c: this.a + this.b  // Doesn't work
};

現在の形式では、は参照thisしないため、このコードは明らかに参照エラーをスローしますfoo。しかし、オブジェクトリテラルのプロパティの値を、以前に宣言された他のプロパティに依存させる方法ありますか?

回答:


744

さて、私があなたに話すことができる唯一のことはゲッターです:

var foo = {
  a: 5,
  b: 6,
  get c() {
    return this.a + this.b;
  }
}

console.log(foo.c) // 11

これはECMAScript 5th Edition仕様で導入された構文拡張であり、構文はほとんどの最新ブラウザー(IE9を含む)でサポートされています。


32
非常に役立つ答え。「取得」に関する詳細情報はここで見つけることができます:developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/...
ジェイクを

5
@FaustoR。はい、そうです。
チャーリーマーティン

48
このソリューションでは、foo.aまたはの値foo.bが変更された場合、の値foo.cも同期して変更されることに注意してください。これが必要な場合とそうでない場合があります。
HBP 2015年

8
thisは最もネストされたオブジェクトにバインドすることに注意してください。例:... x: { get c () { /*this is x, not foo*/ } } ...
Z. Khullah

3
上記のステートメントを完了するために、foobeeingは変数として宣言され、c呼び出されたときにのみ評価されるため、(ただし注意してください)とは対照的に、fooinside cを使用すると機能しthisます
Z. Khullah

316

あなたは次のようなことをすることができます:

var foo = {
   a: 5,
   b: 6,
   init: function() {
       this.c = this.a + this.b;
       return this;
   }
}.init();

これは、オブジェクトの初期化のようなものです。

実際にはinit()to の戻り値を割り当てていることに注意してください。fooしたがって、する必要がありますreturn this


95
それが汚染されないようにdelete this.init前にすることもできますreturn thisfoo
ビリー・ムーン

15
@BillyMoon:はい、確かにそうしますが、そうすると、多くのエンジン(V8など)で、そのオブジェクトに対する後続のすべてのプロパティアクセスのパフォーマンス影響します。
TJクラウダー2014年

8
@MuhammadUmer:ES6クラスが質問にどのように関連しているかはわかりません。
Felix Kling

8
@MuhammadUmer:クラスはコンストラクター関数の単なる構文上のシュガーなので、実際には新しいものは何も提供されません。どちらにしても、この質問の主な焦点はオブジェクトリテラルです。
Felix Kling

3
@akantoword:すばらしい:)オブジェクトリテラルは単一の式であるため、init()呼び出しにはリテラルが直接追加され、単一の式が保持されます。もちろん、必要に応じて関数を個別に呼び出すこともできます。
Felix Kling

181

明白で単純な答えはありません。

しかし、オブジェクトリテラルのプロパティの値を、以前に宣言された他のプロパティに依存させる方法ありますか?

いいえ。ここでのすべてのソリューションは、オブジェクトが(さまざまな方法で)作成されるまで延期し、3番目のプロパティを割り当てます。最も簡単な方法はこれを行うことです。

var foo = {
    a: 5,
    b: 6
};
foo.c = foo.a + foo.b;

他のすべては同じことをするためのより間接的な方法です。(Felixは特に賢いですが、一時的な関数の作成と破棄を必要とし、複雑さを追加します。さらに、オブジェクトに追加のプロパティを残すか、[ deleteそのプロパティの場合] そのオブジェクトに対する後続のプロパティアクセスのパフォーマンス影響を与えます。)

すべてを1つの式内に収める必要がある場合は、一時的なプロパティなしで行うことができます。

var foo = function(o) {
    o.c = o.a + o.b;
    return o;
}({a: 5, b: 6});

または、もちろん、これを複数回行う必要がある場合:

function buildFoo(a, b) {
    var o = {a: a, b: b};
    o.c = o.a + o.b;
    return o;
}

次に、それを使用する必要がある場所:

var foo = buildFoo(5, 6);

私自身の正気のために、私は基本的に同じことを言っているある種の公式ドキュメントを見つけようとしています-オブジェクトthisはそのオブジェクトのメソッドでのみ利用でき、他の種類のプロパティは利用できないということです。私がそれを見つけることができるアイデアはありますか?ありがとう!
デビッドケネル2018

1
@DavidKennell:仕様よりも公式になりません。:-)あなたはおそらくここから始め、それに従います。これはかなりおかしな言語ですが、基本的にはプロパティ定義評価のさまざまな節で、オブジェクトがプロパティ初期化子の値を決定する操作に使用できないことがわかります。
TJクラウダー2018

ここbrowserscopeの結果が表示されませんが、これは正しくありません。私の環境では、v8:deleteは10%高速で、gecko:deleteはわずか1%低速です。
TheMaster

1
@TheMaster-ええ、私はBrowserScopeがもう本当に重要だとは思っていません。削除は以前ほど悪くはないようです。少なくともV8(Chromeなど)やSpiderMonkeyではそうではありません。まだ遅いですが、ほんの少しだけです、そしてこれらの事は最近非常に速いです。
TJクラウダー

63

単に無名関数をインスタンス化します。

var foo = new function () {
    this.a = 5;
    this.b = 6;
    this.c = this.a + this.b;
};

1
@ベルギ、なぜ?誰かが同じオブジェクトの別のインスタンスを作成する可能性があるからですか?オブジェクトリテラルを複製できないだけではありません。これnew Point(x, y)は、関数に再利用のための名前が付けられていないことを除いて、引数を渡すのと同じです。
zzzzBov 2015

1
@zzzzBov:もちろん、オブジェクトのクローンを作成できますが、IEFEソリューション(TJCrowderの回答のような)と比較して、ソリューションはコンストラクター関数をリークし、余分なプロトタイプオブジェクトを作成します。
ベルギ

5
@zzzzBov:var foo = function() { this.…; return this; }.call({});構文的にはそれほど変わらないが、意味的には正解なものを使用します。
ベルギ

1
@Bergi、それが重要だと感じたら、ミックスに独自の答えを追加してみませんか?
zzzzBov 2015

3
あなたはこれを持っています。newキーワードに気づかなかった。
ランディ

26

ES6では、レイジーキャッシュプロパティを作成できます。最初の使用時に、プロパティは1回評価され、通常の静的プロパティになります。結果:2回目の数学関数のオーバーヘッドがスキップされます。

魔法はゲッターにあります。

const foo = {
    a: 5,
    b: 6,
    get c() {
        delete this.c;
        return this.c = this.a + this.b
    }
};

矢印では、ゲッターthis周囲の字句スコープを取得します

foo     // {a: 5, b: 6}
foo.c   // 11
foo     // {a: 5, b: 6 , c: 11}  

1
es5にはObject.defineProperty(foo, 'c', {get:function() {...}})、それらを定義するために使用する必要があるプロパティもあります。これは、このような工場では目立たない方法で簡単に実行できます。もちろん、get砂糖を使うことができればもっと読みやすくなりますが、機能はあります。
Aluan Haddad

21

一部のクロージャはこれに対処する必要があります。

var foo = function() {
    var a = 5;
    var b = 6;
    var c = a + b;

    return {
        a: a,
        b: b,
        c: c
    }
}();

内で宣言されたすべての変数fooはにプライベートfooです。関数の宣言で期待するとおりです。これらはすべてスコープ内にあるためthis、関数で期待するのと同じように、すべてを参照する必要なく相互にアクセスできます。違いは、この関数はプライベート変数を公開するオブジェクトを返し、そのオブジェクトをに割り当てることfooです。最後に、return {}ステートメントを使用してオブジェクトとして公開するインターフェースのみを返します。

関数は最後にで実行され、()これによりfooオブジェクト全体が評価され、インスタンス化されているすべての変数と戻りオブジェクトがのプロパティとして追加されますfoo()


14
これを「クロージャ」と呼ぶのは紛らわしく誤解を招くものです。関数からojbect値を返す正確な意味については意見が異なりますが、誰の書にも閉包はありません。

16

あなたはこのようにそれを行うことができます

var a, b
var foo = {
    a: a = 5,
    b: b = 6,
    c: a + b
}

そのメソッドは、関数が最初に宣言されたオブジェクトを参照する必要があるときに便利であることがわかりました。以下は、私がそれをどのように使用したかを示す最小限の例です。

function createMyObject() {
    var count = 0, self
    return {
        a: self = {
            log: function() {
                console.log(count++)
                return self
            }
        }
    }
}

selfをprint関数を含むオブジェクトとして定義することにより、関数がそのオブジェクトを参照できるようにします。これは、別の場所に渡す必要がある場合に、印刷関数をオブジェクトに「バインド」する必要がないことを意味します。

代わりに、this以下に示すように使用します

function createMyObject() {
    var count = 0
    return {
        a: {
            log: function() {
                console.log(count++)
                return this
            }
        }
    }
}

次に、次のコードは0、1、2をログに記録し、エラーを出力します

var o = createMyObject()
var log = o.a.log
o.a.log().log() // this refers to the o.a object so the chaining works
log().log() // this refers to the window object so the chaining fails!

selfメソッドを使用することにより、関数が実行されたコンテキストに関係なく、printが常に同じオブジェクトを返すことが保証されます。上記のコードは正常に実行され、の自己バージョンを使用すると0、1、2、3が記録されますcreateMyObject()


3
すべてのセミコロンを省略したことを除いて、良い例です。


9

完了のために、ES6にはクラスがあります(これを書いている時点では、最新のブラウザーでのみサポートされていますが、Babel、TypeScript、およびその他のトランスパイラーで使用できます)。

class Foo {
  constructor(){
    this.a = 5;
    this.b = 6;
    this.c = this.a + this.b;
  }  
}

const foo = new Foo();

7

モジュールパターンを使用してそれを行うことができます。と同じように:

var foo = function() {
  var that = {};

  that.a = 7;
  that.b = 6;

  that.c = function() {
    return that.a + that.b;
  }

  return that;
};
var fooObject = foo();
fooObject.c(); //13

このパターンを使用すると、必要に応じて複数のfooオブジェクトをインスタンス化できます。

http://jsfiddle.net/jPNxY/1/


2
これはモジュールパターンの例ではなく、単なる関数です。foo定義の最後の行がの場合}();、それは自己実行し、関数ではなくオブジェクトを返します。また、foo.cは関数であるため、その関数を上書きすると、その関数と次のviaの呼び出しfooObject.c()が失敗します。たぶん、このフィドルはあなたがしようとしているものに近いでしょう(インスタンス化するようには設計されていないシングルトンでもあります)。
Hollister

2
「モジュールパターンはもともと、従来のソフトウェアエンジニアリングのクラスにプライベートとパブリックの両方のカプセル化を提供する方法として定義されていました。」From:JavaScriptデザインパターンの学習。そのオブジェクトは上記のモジュールパターンに従いますが、はパブリックおよびプライベートのプロパティ/メソッドを表示していないため、それを説明するのは最善ではないかもしれません。この1つのjsfiddle.net/9nnR5/2は、パブリックおよびプライベートのプロパティ/メソッドを持つ同じオブジェクトです。したがって、どちらもこのパターンに従っています
Rafael Rocha '16

6

これを行うにはいくつかの方法があります。これは私が使うものです:

function Obj() {
 this.a = 5;
 this.b = this.a + 1;
 // return this; // commented out because this happens automatically
}

var o = new Obj();
o.b; // === 6

2
これは機能しますが、オブジェクトリテラル表記の利点がなくなります。
kpozin

真実、申し訳ありませんが、もともとオブジェクトリテラルタグを逃しました。私は主にデータリテラルにオブジェクトリテラルのみを使用し、追加のロジック(クラスに似ている可能性があります)が必要なときはいつでも、まさにこの理由で関数の結果としてオブジェクトを作成します。

6

念のため-オブジェクトのプロパティをタイムライン外に配置します。

var foo = {
    a: function(){return 5}(),
    b: function(){return 6}(),
    c: function(){return this.a + this.b}
}

console.log(foo.c())

上記にも良い答えがあります。これは、質問したサンプルコードを変更した方法です。

更新:

var foo = {
    get a(){return 5},
    get b(){return 6},
    get c(){return this.a + this.b}
}
// console.log(foo.c);

2
ES6では、この一般的なアプローチをはるかにエレガントにvar foo = { get a(){return 5}, get b(){return 6}, get c(){return this.a + this.b} }することができます。つまり、今foo.cではfoo.c():)の代わりにそれを行うことができます(フォーマットを改善するために、自由にそれを回答に貼り付けてください!)

5

オブジェクトリテラルに新しい関数を作成し、コンストラクターを呼び出すことは、元の問題からの根本的な逸脱のようであり、それは不要です。

オブジェクトリテラルの初期化中に兄弟プロパティを参照することはできません。

var x = { a: 1, b: 2, c: a + b } // not defined 
var y = { a: 1, b: 2, c: y.a + y.b } // not defined 

計算されたプロパティの最も単純なソリューションは次のとおりです(ヒープなし、関数なし、コンストラクタなし)。

var x = { a: 1, b: 2 };

x.c = x.a + x.b; // apply computed property

3

ここに投稿された他の回答の方が優れていますが、次の選択肢があります。

  • 初期化時に値を設定します(ゲッターや派生ではありません)
  • init()オブジェクトリテラル以外のタイプやコードは必要ありません
  • オブジェクトリテラルであり、ファクトリ関数やその他のオブジェクト作成メカニズムではありません。
  • パフォーマンスへの影響はないはずです(初期化時を除く)

自己実行型の無名関数とウィンドウストレージ

var foo = {
    bar:(function(){
        window.temp = "qwert";
        return window.temp;
    })(),
    baz: window.temp
};

順序は保証されますbarbaz)。

windowもちろんそれは汚染さwindow.tempれますが、永続性を必要とするスクリプトを書いている人を想像することはできません。多分tempMyAppあなたが偏執的であるなら。

また、醜いですが、時々役立ちます。たとえば、厳密な初期化条件でAPIを使用していて、リファクタリングをしたくないので、スコープが正しい場合です。

そしてもちろん、それは乾燥しています。


3

これらすべての鍵はSCOPEです。

独自のインスタンス化されたオブジェクトとして定義するプロパティの「親」(親オブジェクト)をカプセル化する必要があります。次に、キーワードを使用して兄弟プロパティへの参照を作成できます。 this

最初にそうせずに参照すると、オブジェクトである外側のスコープを参照することを覚えておくことが非常に重要です。thisthiswindow

var x = 9   //this is really window.x
var bar = {
  x: 1,
  y: 2,
  foo: new function(){
    this.a = 5, //assign value
    this.b = 6,
    this.c = this.a + this.b;  // 11
  },
  z: this.x   // 9 (not 1 as you might expect, b/c *this* refers `window` object)
};

2
これは有効なJSでもありません。

2

オブジェクトがオブジェクトを返す関数として記述されていて、ES6のオブジェクト属性「メソッド」を使用している場合は、次のことが可能です。

const module = (state) => ({
  a: 1,
  oneThing() {
    state.b = state.b + this.a
  },
  anotherThing() {
    this.oneThing();
    state.c = state.b + this.a
  },
});

const store = {b: 10};
const root = module(store);

root.oneThing();
console.log(store);

root.anotherThing();
console.log(store);

console.log(root, Object.keys(root), root.prototype);

2

私は代替として次のコードを使用し、それは動作します。変数も配列にすることができます。(@ Fausto R.)

var foo = {
  a: 5,
  b: 6,
  c: function() {
    return this.a + this.b;
  },

  d: [10,20,30],
  e: function(x) {
    this.d.push(x);
    return this.d;
  }
};
foo.c(); // 11
foo.e(40); // foo.d = [10,20,30,40]

2

ここにES6のきちんとした方法があります:

var foo = (o => ({
    ...o,
    c: o.a + o.b
  }))({
    a: 5,
    b: 6
  });
  
console.log(foo);

私はそれを次のようなことをするために使用します:

const constants = Object.freeze(
  (_ => ({
    ..._,
    flag_data: {
      [_.a_flag]: 'foo',
      [_.b_flag]: 'bar',
      [_.c_flag]: 'oof'
    }
  }))({
    a_flag: 5,
    b_flag: 6,
    c_flag: 7,
  })
);

console.log(constants.flag_data[constants.b_flag]);


1

このソリューションは、配列を含むネストされたオブジェクトでも機能しますか?

      Object.prototype.assignOwnProVal
     = function (to,from){ 
            function compose(obj,string){ 
                var parts = string.split('.'); 
                var newObj = obj[parts[0]]; 
                if(parts[1]){ 
                    parts.splice(0,1);
                    var newString = parts.join('.'); 
                    return compose(newObj,newString); 
                } 
                return newObj; 
            } 
            this[to] = compose(this,from);
     } 
     var obj = { name : 'Gaurav', temp : 
                  {id : [10,20], city:
                        {street:'Brunswick'}} } 
     obj.assignOwnProVal('street','temp.city.street'); 
     obj.assignOwnProVal('myid','temp.id.1');

0

この正確なシナリオがカバーされていないので、オプションを投入しました。あなたがいる場合はありませんしたいcときに更新aまたはb更新、そしてES6生命維持がうまく動作します。

var foo = ((a,b) => ({
    a,
    b,
    c: a + b
}))(a,b);

私のニーズのために、ループで使用される配列に関連するオブジェクトがあるので、いくつかの一般的な設定を一度だけ計算したいので、これは私が持っているものです:

  let processingState = ((indexOfSelectedTier) => ({
      selectedTier,
      indexOfSelectedTier,
      hasUpperTierSelection: tiers.slice(0,indexOfSelectedTier)
                             .some(t => pendingSelectedFiltersState[t.name]),
  }))(tiers.indexOf(selectedTier));

プロパティを設定する必要があり、プロパティをindexOfSelectedTier設定するときにその値を使用する必要があるため、hasUpperTierSelection最初にその値を計算し、パラメーターとしてIIFEに渡します。


0

他のアプローチは、プロパティを割り当てる前に、最初にオブジェクトを宣言することです:

const foo = {};
foo.a = 5;
foo.b = 6;
foo.c = foo.a + foo.b;  // Does work
foo.getSum = () => foo.a + foo.b + foo.c;  // foo.getSum() === 22

これにより、オブジェクト変数名を使用して、すでに割り当てられている値にアクセスできます。ファイルに
最適config.jsです。


これは自己参照ではなく、foo問題のオブジェクトを指す宣言された変数への参照です。
デレクヘンダーソン

0
var x = {
    a: (window.secreta = 5),
    b: (window.secretb = 6),
    c: window.secreta + window.secretb
};

これは、@ slicedtoadの回答とほぼ同じですが、関数を使用しません。


-1

注:このソリューションはTypescriptを使用します(必要に応じてTSがコンパイルするバニラJSを使用できます)

class asd {
    def = new class {
        ads= 'asd';
        qwe= this.ads + '123';
    };

    // this method is just to check/test this solution 
    check(){
        console.log(this.def.qwe);
    }
}

// these two lines are just to check
let instance = new asd();
instance.check();

ここでは、クラス式を使用して、必要なネストされたオブジェクトリテラルインターフェースを取得しています。これは、作成中にオブジェクトのプロパティを参照できるようにするための次善の策です。

注意すべき主な点は、このソリューションを使用している間、オブジェクトリテラルから持っていたのとまったく同じインターフェースを持っているということです。また、構文はオブジェクトリテラル自体に非常に近い(関数を使用するなど)。

以下を比較してください

私が提案した解決策

class asd {
    def = new class {
        ads= 'asd';
        qwe= this.ads + '123';
    };

オブジェクトリテラルで十分な場合の解決策

var asd = {
    def : {
        ads:'asd',
        qwe: this.ads + '123';, //ILLEGAL CODE; just to show ideal scenario
    }
}

もう一つの例

このクラスでは、オブジェクトリテラルでは不可能な、複数の相対パスを相互に組み合わせることができます。

class CONSTANT {
    static readonly PATH = new class {
        /** private visibility because these relative paths don't make sense for direct access, they're only useful to path class
         *
         */
        private readonly RELATIVE = new class {
            readonly AFTER_EFFECTS_TEMPLATE_BINARY_VERSION: fs.PathLike = '\\assets\\aep-template\\src\\video-template.aep';
            readonly AFTER_EFFECTS_TEMPLATE_XML_VERSION: fs.PathLike = '\\assets\\aep-template\\intermediates\\video-template.aepx';
            readonly RELATIVE_PATH_TO_AFTER_EFFECTS: fs.PathLike = '\\Adobe\\Adobe After Effects CC 2018\\Support Files\\AfterFX.exe';
            readonly OUTPUT_DIRECTORY_NAME: fs.PathLike = '\\output';
            readonly INPUT_DIRECTORY_NAME: fs.PathLike = '\\input';
            readonly ASSETS_DIRECTORY_NAME: fs.PathLike = '\\assets';
        };
    }
}

2
それはあなたの答えが全く無関係だからでしょうか?私は反対投票者が説明する必要があることに同意しますが、あなたの答えの明確さは質問とは何の関係もありません…
Manngo

@Manngo指摘してくれてありがとう。正直なところ、私はOPと同じ質問をし、私が提案したソリューションを使用します。不明、なぜそれが無関係と見なされているのか。時間がある場合は説明してください。そうすれば、答えがよくなるか、少なくともどこが間違っているのかを知ることができます。残念ながら、なぜこれが合理的な解決策ではないのか理解できません。
Dheeraj Bhaskar

-2

ネイティブJSを使用する場合は、他の回答が適切な解決策を提供します。

ただし、次のような自己参照オブジェクトを作成する場合は、

{ 
  a: ...,
  b: "${this.a + this.a}",
}

その構文をサポートし、ネイティブオブジェクトを返すself-referenced-objectというnpmライブラリを作成しました。


1
回答のみのリンクは避けてください。「外部サイトへのリンク以上のもの」である回答は削除される場合があります
Quentin

@クエンティン私が私の答えをどのように改善できるかについて何か提案はありますか?この質問に対する他の回答は、ネイティブJavaScriptで自己参照オブジェクトを作成する方法をカバーしていますが、ポスターの元の質問の構文と同様の構文で自己参照オブジェクトを作成する場合は、ライブラリが私は、解決策を探している他の人に役立つかもしれないと書いた。フィードバックをいただければ幸いです。
alex-e-leon

ここで改善すべき点をいくつか挙げます。最初に、そして最も明白に、バックティックなしでテンプレートリテラル構文を使用しています。あなたのbプロパティの値は次のようになります${this.a + this.a}。次に、それほど重要ではありませんが、のようなものを使用して、文字列ではなく数値を返しますparseInt。最後に、最も重要なのは、この例を試したとき、OPが要求するのと同じ理由で、それが単に機能しないことです。this独自のオブジェクト宣言を使用したとき戻っは不定。@ alex-e-leon
アレックドナルドマザー

@AlecDonaldMather-時間を取って見て、フィードバックを提供してくれてありがとう!プロジェクトに興味がある場合は、このディスカッションをgithubに移動することをお勧めしますが、フィードバックの一部に回答することをお勧めします。 objが定義される前にjsが「this」を解決しようとするのを防ぐために、ここではバッククォートが必要です-数値を返します。a+ bがすでに数値の場合、これは機能します。すでに数字。
alex-e-leon

これが未定義を返すので、ライブラリを使用しようとした方法を説明できますか?これは起こらないはずですが、おそらく私が除外したエッジケースがありますか?とは言っても、このライブラリは問題を完全に解決するわけではなく、独自のトレードオフがありますが、改善/使用の支援に興味がある場合はお知らせください!
alex-e-leon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.