对象字面量/初始值设定项中的自引用

IT技术 javascript object-literal
2021-01-04 22:15:11

有什么办法可以让类似下面的东西在 JavaScript 中工作吗?

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

在当前形式中,此代码显然会引发引用错误,因为this没有引用foo. 但是,有什么办法可以在对象文本的属性值依赖于其他属性声明更早?

6个回答

好吧,我唯一能告诉你的是getter

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

console.log(foo.c) // 11

这是 ECMAScript 第 5 版规范引入的语法扩展,大多数现代浏览器(包括 IE9)都支持该语法。

@HBP 这与问题中会发生的事情完全相同,所以在我看来,这正是预期的结果。
2021-02-06 22:15:11
为了完成我上面的陈述,因为foobee 被声明为一个变量并且c只会在它被调用时被评估,所以使用fooinsidec将起作用,而不是this(尽管要小心)
2021-02-14 22:15:11
请注意,this绑定到最深的嵌套对象。例如:... x: { get c () { /*this is x, not foo*/ } } ...
2021-02-27 22:15:11
请注意,使用此解决方案,如果foo.a的值foo.b发生更改,则 的值foo.c也会同步更改。这可能是也可能不是所需要的。
2021-03-05 22:15:11
非常有帮助的答案。更多关于“get”的信息可以在这里找到:developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/...
2021-03-06 22:15:11

你可以这样做:

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

这将是对象的某种一次性初始化。

请注意,您实际上是在分配init()to的返回值foo,因此您必须return this

@BillyMoon:确实如此,尽管这样做会影响对该对象的所有后续属性访问的性能,但在许多引擎(例如 V8)上。
2021-02-10 22:15:11
@MuhammadUmer:不确定 ES6 类如何与问题相关。
2021-02-13 22:15:11
@MuhammadUmer:类只是构造函数的语法糖,所以它们并没有真正提供任何新的东西。无论哪种方式,这个问题的主要焦点都是对象文字。
2021-02-14 22:15:11
@akantoword:太好了 :) 由于对象字面量是单个表达式,init()因此直接调用附加到字面量以使其保持为单个表达式。但是当然您可以根据需要单独调用该函数。
2021-02-23 22:15:11
你也可以delete this.init之前return this这样foo就不会被污染
2021-02-25 22:15:11

缺少显而易见的简单答案,因此为了完整性:

但是,有什么办法可以在对象文本的属性值依赖于其他属性声明更早?

不。这里的所有解决方案都将它推迟到创建对象之后(以各种方式),然后分配第三个属性。最简单的方法是只是这样做:

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

所有其他人只是做同样事情的更间接的方式。(Felix 的特别聪明,但需要创建和销毁一个临时函数,增加了复杂性;并且要么在对象上留下额外的属性,要么[如果你有delete那个属性]影响对该对象后续属性访问的性能。)

如果您需要将其全部放在一个表达式中,则可以在没有临时属性的情况下执行此操作:

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);
在此处看不到browserscope 结果,但情况并非如此,对吗?在我的环境中,v8:delete快了 10%,gecko:delete慢了 1%。
2021-02-09 22:15:11
@TheMaster - 是的,我认为 BrowserScope 不再是一回事了。看起来删除不像以前那么糟糕,至少在 V8(Chrome 等)或 SpiderMonkey 中不是。仍然更慢,但只有一点点,而且这些天这些东西快得惊人。
2021-02-16 22:15:11
@DavidKennell:没有比规范更官方的:-) 您可能会从这里开始并遵循它。这是一种相当笨拙的语言,但基本上您会在属性定义评估的各个子条款中看到该对象不可用于确定属性初始值设定项的值的操作。
2021-02-25 22:15:11
为了我自己的理智,我试图找到某种官方文档,这些文档基本上说的是相同的东西 - 一个对象this只能用于该对象的方法,而不能用于其他类型的属性。知道在哪里可以找到吗?谢谢!
2021-03-05 22:15:11

简单地实例化一个匿名函数:

var foo = new function () {
    this.a = 5;
    this.b = 6;
    this.c = this.a + this.b;
};
@zzzzBov:当然,他们可以只克隆对象,但与 IEFE 解决方案(如 TJCrowder 的回答)相比,您的解决方案泄漏了构造函数并创建了一个多余的原型对象。
2021-02-11 22:15:11
你有这个。我确实没有注意到new关键字。
2021-02-19 22:15:11
@zzzzBov:只需使用var foo = function() { this.…; return this; }.call({});在语法上没有太大不同但语义上合理的。
2021-02-25 22:15:11
@Bergi,为什么?因为有人可能会从中实例化另一个相同的对象?并不是说他们不能只是克隆一个对象文字。这与传递参数没有什么不同,new Point(x, y)只是该函数没有命名以供重用。
2021-03-05 22:15:11
@Bergi,如果您觉得这很重要,为什么不添加您自己的答案呢?
2021-03-08 22:15:11

现在在 ES6 中你可以创建惰性缓存属性。首次使用时,该属性会评估一次以成为正常的静态属性。结果:第二次跳过数学函数开销。

魔法就在吸气剂中。

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

在箭头 getter 中this拾取周围的词法范围

foo     // {a: 5, b: 6}
foo.c   // 11
foo     // {a: 5, b: 6 , c: 11}  
这工作得很好,但我可以知道你为什么要删除它甚至不存在的 this.c 吗?我试过不写,delete this.c但没有用
2021-03-02 22:15:11
es5 还具有您只需要Object.defineProperty(foo, 'c', {get:function() {...}})用来定义它们的属性在像这样的工厂中,这很容易以一种不显眼的方式完成。当然,如果您可以使用get糖,它会更具可读性,但功能已经存在。
2021-03-03 22:15:11