对象常量/初始化程序中的自引用

JavaScript

村村阿飞

2020-03-11

有什么办法可以使以下内容在JavaScript中起作用?

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

在当前形式下,此代码显然会引发引用错误,因为this未引用foo但是,是否有任何方法可以使对象文字的属性值取决于先前声明的其他属性?

第690篇《对象常量/初始化程序中的自引用》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

20个回答
斯丁A 2020.03.11

如果要使用本机JS,则其他答案将提供良好的解决方案。

但是,如果您希望编写类似以下内容的自引用对象:

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

我编写了一个名为自我引用对象的npm库,该库支持该语法并返回一个本机对象。

GOItachi 2020.03.11

因为我没有看到这个确切的场景,所以选择了一个选项。如果你不做c更新时ab更新,那么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

猿西门Tom 2020.03.11
var x = {
    a: (window.secreta = 5),
    b: (window.secretb = 6),
    c: window.secreta + window.secretb
};

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

GreenGil 2020.03.11

另一种方法是在分配属性之前先声明对象:

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文件。

StafanItachi 2020.03.11

我使用以下代码作为替代,并且可以正常工作。并且变量也可以是数组。(@福斯托·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]
Sam番长逆天 2020.03.11

如果将您的对象编写为返回对象的函数,并且使用ES6对象属性“方法”,则可能:

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);
斯丁逆天A 2020.03.11

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

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

自执行匿名功能和窗口存储

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

订单得到保证bar之前baz)。

window当然会污染,但我无法想象有人编写需要window.temp持久化的脚本也许tempMyApp你是偏执狂。

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

当然是干的。

Tom卡卡西 2020.03.11

这一切的关键是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)
};
Jim小小 2020.03.11

只是出于思想考虑-将对象的属性放在时间轴之外:

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);
老丝阿飞 2020.03.11

您可以使用模块模式来实现。就像:

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/

LSam 2020.03.11

在对象文字上创建新函数并调用构造函数似乎与原始问题大相径庭,这是不必要的。

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

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
西里猿LEY 2020.03.11

为了完成,在ES6中我们有一些类(仅在最新的浏览器中才支持,但在Babel,TypeScript和其他编译器中可用)

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

const foo = new Foo();
西里神奇 2020.03.11

你可以这样

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定义为包含打印功能的对象,可以允许该功能引用该对象。这意味着如果您需要将打印功能传递到其他对象,则无需将其“绑定”到对象。

如果可以的话,请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始终返回相同的对象,而不管函数在哪个上下文中运行。当使用的自我版本时,上面的代码可以正常运行并记录0、1、2和3 createMyObject()

逆天梅 2020.03.11

有几种方法可以做到这一点。这就是我会用的:

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
Harry乐Harry 2020.03.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()

西里老丝古一 2020.03.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}  
小小乐 2020.03.11

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

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

好吧,我唯一能告诉你的就是吸气剂:

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

console.log(foo.c) // 11

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

猴子阳光 2020.03.11

缺少显而易见的简单答案,因此出于完整性考虑:

但是,是否有任何方法可以使对象文字的属性值取决于先前声明的其他属性?

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

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

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

If you need it to all be within one expression, you can do that without the temporary property:

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

Or of course, if you need to do this more than once:

function buildFoo(a, b) {
    var o = {a: a, b: b};
    o.c = o.a + o.b;
    return o;
}

then where you need to use it:

var foo = buildFoo(5, 6);
小哥Stafan 2020.03.11

您可以执行以下操作:

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

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

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

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android