web-dev-qa-db-ja.com

オブジェトリテラル/初期化子における自己参照

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

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

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

606
kpozin

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

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

foo.c; // 11

これはECMAScript第5版仕様によって導入された構文拡張であり、その構文はほとんどの最近のブラウザ(IE 9を含む)によってサポートされています。

609
CMS

次のようなことができます。

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

これは、オブジェクトのある種の1回限りの初期化です。

実際にはinit()の戻り値をfooに代入しているので、return thisを使用する必要があります。

292
Felix Kling

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

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

いいえ。ここでのすべての解決策は、(さまざまな方法で)オブジェクトが作成されるまでそれを延期してから、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);
162
T.J. Crowder

無名関数を単にインスタンス化する:

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

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}  
19
voscausa

いくらかの閉鎖はこれに対処するべきです。

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()のプロパティとして追加します。

19
Henry Wrightson

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

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
            }
        }
    }
}

自分自身をprint関数を含むオブジェクトとして定義することで、関数にそのオブジェクトを参照させることができます。つまり、他の場所に渡す必要がある場合は、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は常に同じオブジェクトを返すことを保証します。 createMyObject()の自己バージョンを使うとき、上のコードはちょうどうまく動き、0、1、2、3を記録します。

15
davedambonite

完成のために、ES6にはクラスがあります(これを書いている時点では最新のブラウザでしかサポートされていませんが、Babel、TypeScriptそして他のtranspilerで利用可能です)

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

const foo = new Foo();
8
monzonj

これを実現するにはいくつかの方法があります。これは私が使用するものです:

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
7
ken

あなたはモジュールパターンを使ってそれをすることができます。と同じように:

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/

7
Rafael Rocha

考えているだけのために - タイムラインの外にオブジェクトのプロパティを配置します。

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);
6
animaacija

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

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

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
4
Rick O'Shea

これらすべての秘訣はSCOPEです。

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

最初にそうせずにthisを参照する場合、thisは外側のスコープを参照することになることを覚えておくことは非常に非常に非常に重要ですwindowオブジェクト.

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)
};
3
user3606367

ここに掲載されている他の答えはより優れていますが、これは代替案です:

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

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

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

順序は 保証 です(barの前にbaz)。

もちろんそれはwindowを汚染しますが、私は誰かがwindow.tempを永続的であることを要求するスクリプトを書くことを想像することはできません。あなたが妄想的であればたぶんtempMyApp

それはまた醜いけれども時折役に立つ。例としては、厳密な初期化条件でAPIを使用していて、リファクタリングをしたくないためスコープが正しいことが挙げられます。

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

2
slicedtoad

あなたのオブジェクトがオブジェクトを返す関数として書かれていて、そしてあなたがES6オブジェクト属性 'methods'を使用しているなら、それは可能です:

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
daviestar

これはきちんとした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]);
2
UDrake

この解決策については、これは配列を持つ入れ子になったオブジェクトでもうまくいくでしょう

      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');
1
Gaurav

私は代わりに以下のコードを使用します、そしてそれは働きます。そして変数も配列にすることができます。 (@ファウスト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]
1
Indiana

別の方法として、物事を少し単純にしてオブジェクトに新しい関数を定義しない場合は、元のオブジェクトをそのプロパティと一緒に保持し、動的プロパティを持つ2番目のオブジェクトを作成する最初のオブジェクトのプロパティを使用してから、 spread を使用してマージします。

そう:

オブジェクト

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

オブジェクト2

var foo2 = {
    c: foo.a + foo.b
};

マージ

Object.assign(foo, foo2);

または

foo = {...foo, ...foo2};

fooの結果:

{ 
  "a":5,
  "b":6,
  "c":11
}

すべて同じjs内にあり、オブジェクト内に新しい関数はありません。割り当てまたは展開のみを使用します。

0
c-chavez

他の方法は、プロパティを割り当てる前に、まずオブジェクトを宣言することです。

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ファイルに最適。

0
5ervant

私はこの正確なシナリオが網羅されているのを見なかったのでオプションを投入します。 cまたはaの更新時にdon'tbを更新したい場合は、ES6 IIFEが適しています。

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
Snekse