有没有办法在JavaScript中使用以下内容?
var foo = {
a: 5,
b: 6,
c: this.a + this.b // Doesn't work
};
在当前表单中,此代码显然会引发引用错误,因为this
未引用foo
。但是有没有办法让对象文字的属性中的值依赖于之前声明的其他属性?
答案 0 :(得分:602)
嗯,我唯一可以告诉你的是吸气剂:
var foo = {
a: 5,
b: 6,
get c () {
return this.a + this.b;
}
};
foo.c; // 11
这是ECMAScript第5版规范引入的语法扩展,大多数现代浏览器(包括IE9)都支持该语法。
答案 1 :(得分:292)
您可以执行以下操作:
var foo = {
a: 5,
b: 6,
init: function() {
this.c = this.a + this.b;
return this;
}
}.init();
这将是对象的某种初始化。
请注意,您实际上是将init()
的返回值分配给foo
,因此您必须return this
。
答案 2 :(得分:160)
缺少明显,简单的答案,所以为了完整性:
但是有没有办法让对象文字的属性中的值依赖于之前声明的其他属性?
没有。这里的所有解决方案都将其推迟到创建对象之后(以各种方式),然后分配第三个属性。 最简单的方法就是这样做:
var foo = {
a: 5,
b: 6
};
foo.c = foo.a + foo.b;
所有其他人只是更间接的方式来做同样的事情。 (Felix's特别聪明,但需要创建和销毁临时函数,增加复杂性;要么在对象上留下额外的属性,要么[如果你delete
后续属性访问var foo = function(o) {
o.c = o.a + o.b;
return o;
}({a: 5, b: 6});
属性impacts the performance对象。)
如果你需要它在一个表达式中,你可以在没有临时属性的情况下完成:
function buildFoo(a, b) {
var o = {a: a, b: b};
o.c = o.a + o.b;
return o;
}
当然,如果您需要不止一次这样做:
var foo = buildFoo(5, 6);
然后你需要使用它:
{{1}}
答案 3 :(得分:59)
只需实例化一个匿名函数:
var foo = new function () {
this.a = 5;
this.b = 6;
this.c = this.a + this.b;
};
答案 4 :(得分:19)
现在在ES6中,您可以创建延迟缓存属性。首次使用时,属性将评估一次以成为正常的静态属性。结果:第二次跳过数学函数开销。
魔法在吸气剂中。
const foo = {
a: 5,
b: 6,
get c() {
delete this.c;
return this.c = this.a + this.b
}
};
在箭头getter this
中选择surrounding lexical scope。
foo // {a: 5, b: 6}
foo.c // 11
foo // {a: 5, b: 6 , c: 11}
答案 5 :(得分:19)
有些关闭应该处理这个问题;
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()
的属性添加。
答案 6 :(得分:15)
你可以这样做
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始终返回相同的对象,而不管运行该函数的上下文。当使用createMyObject()
的自我版本时,上面的代码将运行正常并记录0,1,2和3。
答案 7 :(得分:8)
为了完成,在ES6中我们已经有了类(仅在最新浏览器编写时支持,但在Babel,TypeScript和其他转换器中可用)
class Foo {
constructor(){
this.a = 5;
this.b = 6;
this.c = this.a + this.b;
}
}
const foo = new Foo();
答案 8 :(得分: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对象。
答案 9 :(得分:7)
答案 10 :(得分: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);
答案 11 :(得分:4)
在对象文字上创建新函数并调用构造函数似乎与原始问题完全不同,并且它是不必要的。
在对象文字初始化期间,您无法引用同级属性。
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
答案 12 :(得分:3)
所有这一切的关键是 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)
};
答案 13 :(得分:2)
此处发布的其他答案更好,但这里有一个替代方案:
init()
或代码文字之外的代码自动执行匿名函数和窗口存储
var foo = {
bar:(function(){
window.temp = "qwert";
return window.temp;
})(),
baz: window.temp
};
订单为guaranteed(bar
之前baz
)。
它当然会污染window
,但我无法想象某人编写需要window.temp
持久化的脚本。如果你是偏执狂,也许tempMyApp
。
它也很难看,但偶尔也很有用。例如,当您使用具有严格初始化条件的API并且不想重构时,因此范围是正确的。
当然,它很干燥。
答案 14 :(得分: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]);
答案 15 :(得分:1)
我使用以下代码作为替代,它可以工作。变量也可以是数组。 (@ 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]
答案 16 :(得分: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');
答案 17 :(得分:1)
var
属性的效果很好,您还可以对仅运行一次的“昂贵”功能使用绑定闭包(这仅适用于const
,不适用于{{1 }}或let
)
var info = {
address: (function() {
return databaseLookup(this.id)
}).bind(info)(),
get fullName() {
console.log('computing fullName...')
return `${this.first} ${this.last}`
},
id: '555-22-9999',
first: 'First',
last: 'Last',
}
function databaseLookup() {
console.log('fetching address from remote server (runs once)...')
return Promise.resolve(`22 Main St, City, Country`)
}
// test
(async () => {
console.log(info.fullName)
console.log(info.fullName)
console.log(await info.address)
console.log(await info.address)
console.log(await info.address)
console.log(await info.address)
})()
答案 18 :(得分:1)
其他方法是在向对象分配属性之前先声明该对象:
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
文件。
答案 19 :(得分:0)
这里已经有很好的答案,我不是这方面的专家,但我是懒惰的专家,在我的专家眼中,这些答案似乎不够懒惰。
与T.J. Crowder、Henry Wrightson 和Rafael Rocha 的答案略有不同:
var foo = (() => {
// Paste in your original object
const foo = {
a: 5,
b: 6,
};
// Use their properties
foo.c = foo.a + foo.b;
// Do whatever else you want
// Finally, return object
return foo;
})();
console.log(foo);
这里的小优势是只是按原样粘贴原始对象,而不必担心参数等(恕我直言,包装函数通过这种方式变得非常透明)。
如果您不立即需要 foo.c
,这里可能会起作用:
var foo = {
a: 5,
b: 6,
c: setTimeout(() => foo.c = foo.a + foo.b, 0)
};
// Though, at first, foo.c will be the integer returned by setTimeout
console.log(foo);
// But if this isn't an issue, the value will be updated when time comes in the event loop
setTimeout( () => console.log(foo), 0);
答案 20 :(得分:0)
var x = {
a: (window.secreta = 5),
b: (window.secretb = 6),
c: window.secreta + window.secretb
};
这与@slicedtoad的答案几乎相同,但不使用函数。
答案 21 :(得分:0)
投入选项,因为我没有看到这个确切的情况。如果您在c
或a
更新时不希望b
更新,那么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
答案 22 :(得分:-1)
如果要使用本机JS,其他答案将提供很好的解决方案。
但是,如果您希望编写类似以下内容的自引用对象:
{
a: ...,
b: "${this.a + this.a}",
}
我编写了一个名为self-referenced-object的npm库,该库支持该语法并返回一个本机对象。
答案 23 :(得分:-1)
另一种方法是使原始对象及其属性保持不变,并创建具有动态属性的第二个对象,以使事情变得更简单并且不在对象中定义新功能。第一个对象的属性,然后使用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中,对象中没有新功能,仅使用分配或传播。
答案 24 :(得分:-3)
注意:该解决方案使用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';
};
}
}