次のようなものをJavaScriptで機能させる方法はありますか?
var foo = {
a: 5,
b: 6,
c: this.a + this.b // Doesn't work
};
現在の形式では、this
はfoo
を参照しないため、このコードは明らかに参照エラーをスローします。しかし、はオブジェクトリテラルのプロパティの値を、以前に宣言された他のプロパティに依存させる方法はありますか?
さて、私があなたに言うことができる唯一のものはゲッターです:
var foo = {
a: 5,
b: 6,
get c () {
return this.a + this.b;
}
};
foo.c; // 11
これはECMAScript第5版仕様によって導入された構文拡張であり、その構文はほとんどの最近のブラウザ(IE 9を含む)によってサポートされています。
次のようなことができます。
var foo = {
a: 5,
b: 6,
init: function() {
this.c = this.a + this.b;
return this;
}
}.init();
これは、オブジェクトのある種の1回限りの初期化です。
実際にはinit()
の戻り値をfoo
に代入しているので、return this
を使用する必要があります。
明白で単純な答えはありません。
しかし、はオブジェクトリテラルのプロパティの値を、以前に宣言された他のプロパティに依存させる方法はありますか。
いいえ。ここでのすべての解決策は、(さまざまな方法で)オブジェクトが作成されるまでそれを延期してから、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);
無名関数を単にインスタンス化する:
var foo = new function () {
this.a = 5;
this.b = 6;
this.c = this.a + this.b;
};
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}
いくらかの閉鎖はこれに対処するべきです。
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()
のプロパティとして追加します。
あなたはこれのようにそれをすることができます
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を記録します。
完成のために、ES6にはクラスがあります(これを書いている時点では最新のブラウザでしかサポートされていませんが、Babel、TypeScriptそして他のtranspilerで利用可能です)
class Foo {
constructor(){
this.a = 5;
this.b = 6;
this.c = this.a + this.b;
}
}
const foo = new Foo();
これを実現するにはいくつかの方法があります。これは私が使用するものです:
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
あなたはモジュールパターンを使ってそれをすることができます。と同じように:
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オブジェクトを具体化することができます。
考えているだけのために - タイムラインの外にオブジェクトのプロパティを配置します。
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);
オブジェクトリテラルに新しい関数を作成してコンストラクタを呼び出すことは、元の問題から根本的に逸脱しているように思われます。それは不要です。
オブジェクトリテラルの初期化中に兄弟プロパティを参照することはできません。
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
これらすべての秘訣は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)
};
ここに掲載されている他の答えはより優れていますが、これは代替案です:
init()
やコードを必要としません自己実行型の無名関数とウィンドウストレージ
var foo = {
bar:(function(){
window.temp = "qwert";
return window.temp;
})(),
baz: window.temp
};
順序は 保証 です(bar
の前にbaz
)。
もちろんそれはwindow
を汚染しますが、私は誰かがwindow.temp
を永続的であることを要求するスクリプトを書くことを想像することはできません。あなたが妄想的であればたぶんtempMyApp
。
それはまた醜いけれども時折役に立つ。例としては、厳密な初期化条件でAPIを使用していて、リファクタリングをしたくないためスコープが正しいことが挙げられます。
そしてそれはもちろん乾燥しています。
あなたのオブジェクトがオブジェクトを返す関数として書かれていて、そしてあなたが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);
これはきちんとした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]);
この解決策については、これは配列を持つ入れ子になったオブジェクトでもうまくいくでしょう
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');
私は代わりに以下のコードを使用します、そしてそれは働きます。そして変数も配列にすることができます。 (@ファウスト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番目のオブジェクトを作成する最初のオブジェクトのプロパティを使用してから、 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内にあり、オブジェクト内に新しい関数はありません。割り当てまたは展開のみを使用します。
他の方法は、プロパティを割り当てる前に、まずオブジェクトを宣言することです。
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
ファイルに最適。
私はこの正確なシナリオが網羅されているのを見なかったのでオプションを投入します。 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にパラメータとして渡します。