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

Self-references in object literals / initializers

提问人:kpozin 提问时间:1/6/2011 最后编辑:T.J. Crowderkpozin 更新时间:9/13/2023 访问量:208305

问:

有没有办法让类似以下内容的东西在 JavaScript 中工作?

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

在当前形式中,此代码显然会引发引用错误,因为它不引用 .但是有没有办法让对象文字属性中的值依赖于之前声明的其他属性?thisfoo

JavaScript 对象文字

评论


答:

1019赞 Christian C. Salvadó 1/6/2011 #1

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

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

console.log(foo.c) // 11

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

评论

38赞 jake 2/3/2013
非常有帮助的答案。有关“获取”的更多信息,请访问:developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/...
69赞 HBP 5/2/2015
请注意,使用此解决方案时,如果 or 的值发生变化,则 的值也将同步更改。这可能是也可能不是必需的。foo.afoo.bfoo.c
4赞 Randy 3/11/2016
@HBP 这与问题中发生的事情完全相同,所以在我看来,这正是预期的结果。
17赞 Bernardo Dal Corno 4/19/2018
请注意,绑定到最深的嵌套对象。例如:this... x: { get c () { /*this is x, not foo*/ } } ...
3赞 Bernardo Dal Corno 4/20/2018
为了完成我上面的陈述,由于 beeing 被声明为变量并且只会在调用它时进行评估,因此使用 inside 将起作用,而不是(尽管要小心)foocfoocthis
352赞 Felix Kling 1/6/2011 #2

你可以做这样的事情:

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

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

请注意,您实际上是在为 的返回值赋值,因此您必须 。init()fooreturn this

评论

111赞 Billy Moon 7/26/2011
你也可以之前,这样就不会被污染delete this.initreturn thisfoo
17赞 T.J. Crowder 5/19/2014
@BillyMoon:确实如此,尽管这样做会影响该对象上所有后续属性访问的性能,例如在许多引擎(例如 V8)上。
9赞 Felix Kling 6/1/2015
@MuhammadUmer:不确定 ES6 类与问题的关系如何。
9赞 Felix Kling 6/1/2015
@MuhammadUmer:类只是构造函数的语法糖,所以它们并没有真正提供任何新的东西。无论哪种方式,这个问题的主要焦点都是对象文字。
3赞 Felix Kling 7/27/2016
@akantoword:很棒的:)由于对象文本是单个表达式,因此调用直接附加文本以使其保留为单个表达式。但是,当然,您可以根据需要单独调用该函数。init()
6赞 ken 1/7/2011 #3

有几种方法可以实现此目的;这是我会使用的:

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 1/16/2011
这可行,但消除了对象文字表示法的优点。
0赞 ken 1/16/2011
没错,对不起,我最初错过了 object-literal 标签。我大多只对数据结构使用对象文字,每当我想要任何额外的逻辑(可能类似于类)时,我都会出于这个原因创建对象作为函数的结果。
228赞 T.J. Crowder 5/26/2012 #4

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

但是有没有办法让对象文字属性中的值依赖于之前声明的其他属性?

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

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

评论

0赞 David Kennell 5/5/2018
为了我自己的理智,我试图找到某种官方文档,这些文档基本上都说了同样的事情——一个对象的只可用于该对象的方法,而没有其他类型的属性。知道我在哪里可以找到吗?谢谢!this
1赞 T.J. Crowder 5/5/2018
@DavidKennell:没有比规范更正式了。:-)你可能会从这里开始并坚持到底。这是相当笨拙的语言,但基本上你会在属性定义评估的各个子句中看到,该对象不可用于确定属性初始值设定项值的操作。
0赞 TheMaster 5/7/2020
在这里看不到浏览器范围结果,但现在已经不是这样了,对吧?在我的环境中,v8: 快了 10%,而 gecko : 只慢了 1%。deletedelete
1赞 T.J. Crowder 5/8/2020
@TheMaster - 是的,我不认为 BrowserScope 真的不再是一回事了。看起来删除并不像以前那么糟糕,至少在 V8(Chrome 等)或 SpiderMonkey 中不会。仍然很慢,但只有一点点,这些天这些东西的速度快得离谱。
7赞 Rafael Rocha 1/8/2013 #5

您可以使用模块模式来完成此操作。就像:

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赞 Hollister 12/26/2013
这不是模块模式的示例,而只是一个函数。如果 foo 定义的最后一行是 ,它将自行执行并返回一个对象,而不是一个函数。此外,是一个函数,因此写入它会破坏该函数,并且下一次调用 via 将失败。也许这个小提琴更接近你想要的(它也是一个单例,不是为实例化而设计的)。}();foo.cfooObject.c()
2赞 Rafael Rocha 1/17/2014
“模块模式最初被定义为一种为传统软件工程类提供私有和公共封装的方法”。来自:学习 JavaScript 设计模式。该对象遵循上述模块模式,但也许它不是解释这一点的最佳对象,因为它没有显示公共和私有属性/方法。这个 jsfiddle.net/9nnR5/2 是具有公共和私有属性/方法的同一对象。所以他们俩都遵循这种模式
71赞 zzzzBov 4/12/2013 #6

只需实例化一个匿名函数:

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

评论

1赞 zzzzBov 8/2/2015
@Bergi,为什么?因为有人可能会从中实例化另一个相同的对象?这并不是说他们不能只克隆一个对象文字。这与传递参数没有什么不同,只是该函数没有为重用而命名。new Point(x, y)
1赞 Bergi 8/2/2015
@zzzzBov:当然,他们可以克隆对象,但与IEFE解决方案相比(如TJCrowder的回答),您的解决方案会泄漏构造函数并创建一个多余的原型对象。
8赞 Bergi 8/2/2015
@zzzzBov:只是使用在句法上没有太大区别,但在语义上是理智的。var foo = function() { this.…; return this; }.call({});
1赞 zzzzBov 8/2/2015
@Bergi,如果你觉得它很重要,为什么不把你自己的答案加入其中呢?
3赞 Randy 3/11/2016
你有这个。我确实没有注意到这个关键词。new
18赞 davedambonite 5/25/2013 #7

你可以这样做

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 都将始终返回相同的对象。上面的代码运行良好,并在使用 self 版本的 时记录 0、1、2 和 3。createMyObject()

27赞 Henry Wrightson 7/2/2013 #8

一些关闭应该处理这个问题;

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

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

正如您对任何函数声明所期望的那样,其中声明的所有变量都是私有的,并且因为它们都在范围内,因此它们都可以相互访问,而无需引用 ,就像您对函数所期望的那样。不同之处在于,此函数返回一个对象,该对象公开私有变量并将该对象分配给 。最后,您只返回要作为语句的对象公开的接口。foofoothisfooreturn {}

然后,该函数在末尾执行,导致整个 foo 对象被计算,实例化中的所有变量,并将返回对象添加为 的属性。()foo()

评论

17赞 9/7/2014
称其为“关闭”是令人困惑和误导的。尽管对确切的含义意见不一,但从函数返回 ojbect 值并不构成任何人的书中的闭包。
2赞 user3606367 5/7/2014 #9

这一切的关键是 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)
};
5赞 Rick O'Shea 9/14/2014 #10

在对象字面上创建新函数并调用构造函数似乎与原始问题完全不同,而且没有必要。

不能在对象文本初始化期间引用同级属性。

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赞 DanielST 3/25/2015 #11

此处发布的其他答案更好,但这里有一个替代方案:

  • 在初始化时设置值(不是 getter 或派生等)
  • 不需要对象文本之外的任何类型或代码init()
  • 是对象文本,而不是工厂函数或其他对象创建机制。
  • 不应对性能产生任何影响(初始化时除外)

自执行匿名函数和窗口存储

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

订单是有保证的(之前)。barbaz

它当然会污染,但我无法想象有人会写一个需要坚持不懈的剧本。也许如果你是偏执行狂。windowwindow.temptempMyApp

它也很丑陋,但偶尔有用。例如,当您使用具有严格初始化条件的 API 并且不想重构时,因此范围是正确的。

当然,它是干燥的。

8赞 animaacija 8/11/2015 #12

只是为了思考 - 将对象的属性放在时间轴之外:

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赞 12/6/2016
在 ES6 中,你可以使这种通用方法更加优雅:所以现在你可以只做而不是:)(随意将其粘贴到您的答案中,以便格式更好!var foo = { get a(){return 5}, get b(){return 6}, get c(){return this.a + this.b} }foo.cfoo.c()
0赞 Mikko Rantalainen 9/29/2022
请注意,每次使用时都会重新计算。这可能是也可能不是你要找的。foo.c
12赞 monzonj 3/28/2016 #13

为了完成,在 ES6 中,我们有类(在编写本文时,只有最新的浏览器支持,但在 Babel、TypeScript 和其他转译器中可用)

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

const foo = new Foo();
4赞 Indiana 6/17/2016 #14

我使用以下代码作为替代方法,它可以工作。变量也可以是数组。(@ 福斯托 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赞 daviestar 8/13/2016 #15

如果你的对象被写成一个返回对象的函数,并且你使用了 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);
42赞 voscausa 11/9/2016 #16

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

评论

1赞 Aluan Haddad 4/18/2017
ES5 还具有您只需要用来定义它们的属性。在像这样的工厂中,这很容易以一种不显眼的方式完成。当然,如果你能使用糖,它的可读性会更高,但功能已经存在。Object.defineProperty(foo, 'c', {get:function() {...}})get
1赞 Ahsan Alii 3/30/2021
这工作得很好,但是我可以知道你为什么要删除this.c,而它甚至不存在吗?我试过不写,但没有用delete this.c
2赞 CTS_AE 1/28/2022
我也在挠头。我认为它正在做的是从对象中删除属性并用标准属性覆盖它。我认为这样它只会计算一次,然后不会更新其值,如果或以后更改,但这也仅在调用时起作用/缓存/计算。deleteget cfoo.cabfoo.c
0赞 voscausa 1/28/2022
是的,这就是所谓的懒惰评估。更多信息请见:en.wikipedia.org/wiki/Lazy_evaluation
0赞 Mikko Rantalainen 9/29/2022
重点是删除吸气剂,然后继续用单个数值替换它。这样做可以避免每次使用时都需要执行 getter。该语句既创建替换属性,又返回其值。可以使用,但每次使用时都会重新评估。delete this.cfoo.creturnfoo.cget c() { return this.a + this + b }foo.c
1赞 Gaurav 11/22/2016 #17

这个解决方案怎么样,这也适用于带有数组的嵌套对象

      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赞 Snekse 2/14/2018 #18

抛出一个选项,因为我没有看到这个确切的场景。如果您不想在更新时更新,那么 ES6 IIFE 可以很好地工作。cab

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

由于我需要为 设置一个属性,并且在设置该属性时需要使用该值,因此我首先计算该值并将其作为参数传递给 IIFEindexOfSelectedTierhasUpperTierSelection

2赞 UDrake 6/13/2018 #19

这里有一个简洁的 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赞 Dheeraj Bhaskar 6/19/2018 #20

注意:此解决方案使用 Typescript(如果需要,您可以使用 TS 编译的 vanilla 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';
        };
    }
}

评论

3赞 Manngo 7/8/2018
难道是因为你的答案完全无关紧要吗?我同意反对者应该解释,但你的答案与问题无关......
0赞 Dheeraj Bhaskar 7/9/2018
@Manngo感谢您指出这一点。老实说,我和 OP 有同样的问题,我使用我建议的解决方案。不确定,为什么它被认为是无关紧要的。如果你有时间,请解释一下,这样我就可以更好地回答,或者至少知道我错在哪里。不幸的是,我不明白为什么这不是一个合理的解决方案。
0赞 Abrar Hossain 3/10/2021
这根本无法解决自我引用的问题。您提出的是一种相当复杂的模拟自我引用的方法,方法是在代码中引入不必要的闭包。
-1赞 alex-e-leon 4/27/2019 #21

如果你想使用原生 JS,其他答案提供了很好的解决方案。

但是,如果您愿意编写自引用对象,例如:

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

我编写了一个名为 self-referenced-object 的 npm 库,它支持该语法并返回一个本机对象。

评论

1赞 Quentin 5/14/2019
避免仅链接答案。“勉强超过外部网站的链接”的答案可能会被删除
1赞 alex-e-leon 5/15/2019
@Quentin您对如何改进我的答案有什么建议吗?这个问题的其他答案涵盖了如何在本机 javascript 中编写自引用对象,但是如果您想使用类似于海报原始问题中的语法编写自引用对象,我认为我编写的库可能对其他寻找解决方案的人有用。很高兴得到一些反馈。
0赞 Alec Mather 5/13/2020
这里有几件事需要改进。首先,也是最明显的是,您使用的是没有反引号的模板文字语法。您的财产价值应为:。其次,但不太重要的是,您希望使用类似 的东西返回一个数字,而不是字符串。最后也是最重要的一点,当我尝试这个例子时,它根本不起作用,原因与 OP 要求的原因相同。当使用自己的对象声明时,返回 undefined。@alex-e-莱昂b${this.a + this.a}parseIntthis
0赞 alex-e-leon 5/14/2020
@AlecDonaldMather - 感谢您抽出宝贵时间查看并提供一些反馈!如果你对这个项目感兴趣,最好将这个讨论移到 github,但要回答你的一些反馈: - 使用反引号:如之前的评论所述,JS 不支持此语法,因此这里需要使用字符串而不是反引号,以避免 js 在定义 obj 之前尝试解析“this” - 返回一个数字, 如果 A + B 已经是数字,这应该有效,因为如果 A 和 B 都已经是数字,则 A + B 将返回一个数字。
0赞 alex-e-leon 5/14/2020
关于返回未定义,您能解释一下您是如何尝试使用该库的吗?这不应该发生,但也许我遗漏了一个边缘情况?也就是说,这个库并不能完全解决问题,并且有自己的一套权衡,但如果您有兴趣帮助我改进/使用它,请告诉我!
-1赞 5ervant - techintel.github.io 6/9/2019 #22

另一种方法是先声明对象,然后再向对象分配属性:

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

评论

1赞 Derek Henderson 9/10/2019
这不是自我引用,而是对指向相关对象的声明变量的引用。foo
-4赞 David Newberry 10/31/2019 #23
var x = {
    a: (window.secreta = 5),
    b: (window.secretb = 6),
    c: window.secreta + window.secretb
};

这与 @slicedtoad 的答案几乎相同,但没有使用函数。

6赞 anthumchris 11/1/2020 #24

该属性运行良好,您还可以将绑定闭包用于只运行一次的“昂贵”函数(这仅适用于 var,不适用于 constletget

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

0赞 flen 1/6/2021 #25

两种惰性解决方案

这里已经有很好的答案,我不是这方面的专家,但我是懒惰的专家,在我的专家眼中,这些答案似乎还不够懒惰。

第一:从匿名函数返回对象

T.J. CrowderHenry WrightsonRafael 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);

这里的小优势是只是按原样粘贴您的原始对象,而不必担心参数等(恕我直言,包装器函数以这种方式变得非常透明)。

第二种:使用 setTimeout

如果您不需要立即,这里可能会起作用: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);

2赞 i474 11/7/2021 #26

下面是对象中“this”行为的示例。

this.prop = 'external';
global.prop = 'global.prop';
const that = this;

const a = {
  prop: 'internal',
  prop1: this.prop, //external

  log() {
    return this.prop //internal
  },
  log1: () => {
    return this.prop //external
  },
  log2: () => {
    return function () {
      return this.prop; //'global.prop' in node; 'external' in chrome
    }()
  },
  log3: function () {
    return (() => {
      return this.prop; //internal
    })()
  },
}
6赞 Jamesfo 1/26/2022 #27

只供大家娱乐:

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

console.log(foo);

评论

4赞 Royi Namir 4/12/2022
哈哈:-)...........
0赞 Mikko Rantalainen 9/29/2022
有趣的黑客。您需要在第一个前面添加 or 以使其在严格模式下工作。varletThis
0赞 InSync 7/31/2023
@MikkoRantalainen 不,是一个参数(我知道,这并不明显)。它不需要前面的变量声明关键字。This
0赞 Mikko Rantalainen 8/2/2023
哦,我最初将该代码视为使用对象初始值设定项进行定义,据我所知,这些代码总是需要在严格模式下声明。但是,正如您正确指出的那样,这实际上是带有箭头语法的函数定义,并且是函数参数,它只是“碰巧”具有与隐式对象匹配的默认值。我认为这是另一个例子,箭头函数大大降低了 JS 代码的可读性,因为您必须进行大量回溯才能正确解析代码。ThisThis
0赞 Lyokolux 7/5/2022 #28

好的,我想出了另一个解决方案。在这里,我想初始化一个对象,表示每个时间单位的毫秒数。 事实证明,打字稿中的枚举在我的情况下不能使用,所以我声明了分配给对象的多个变量,如下所示:

const SECOND = 1000
const MINUTE = 60 * SECOND
const HOUR = 60 * MINUTE
const DAY = 24 * HOUR
const WEEK = 7 * DAY

export const TimeInMS = {
  SECOND,
  MINUTE,
  HOUR,
  DAY,
  WEEK
}

这种方法的缺点是:

  • 变量被定义为常量,即使我们不需要它们。因此,它需要无用的内存。
  • 对象的每个值都必须声明为独立变量
1赞 Mikko Rantalainen 9/29/2022 #29

我认为以下是可维护性的最佳代码,即使它不是对象文字语法:

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

这将创建一个新的空对象,然后使用匿名函数设置其属性(使用 执行)。我认为唯一不好的部分是需要感觉像是一行额外的代码。不幸的是,我无法想出任何更好的方法来将对新创建的匿名对象的引用移动到 。{}call()return thisfoo

我认为这比语法更好,因为这个语法不会在原型链中创建一个额外的级别,正如现有答案之一的评论中@Bergi所解释的那样。var foo = new function() {...}

但是,如果这真的是字面意思,除了一个补充之外没有任何其他逻辑,那么只写会更有意义

const foo = {
    a:5,
    b:6,
    c:11, // sum of a + b
};

因为没有必要在运行时甚至编译时计算该总和。

0赞 Mikko Rantalainen 9/30/2022 #30

具有相当好可维护性的替代语法:

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

这是有效的,因为如果您不明确指定该名称,JavaScript 将使用变量名称作为新创建对象的属性名称。对于像这样的短数组,我个人会使用单行语法,如果它在一个函数中:return

let a = 5;
let b = 6;
return { a, b, c:a+b };
0赞 Andrew Parks 3/10/2023 #31

尽管对象声明不允许引用先前的属性,但函数声明中的默认值允许引用。

因此,您可以这样做:

const foo = ((a=5, b=6, c=a+b) => ({a,b,c}))()

console.log(foo)

-1赞 michael_vons 9/13/2023 #32

如果你想避免使用这个关键词,你可以在下面使用我的 ES6 方法this

const MEASUREMENT = {
  device_width: 124,
  canvas_padding: 24,
  full_width: () => MEASUREMENT.device_width - MEASUREMENT.canvas_padding,
};