对象字面声明中的自引用

有没有什么办法让下面的东西在JavaScript中工作?

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

在目前的forms下,这段代码显然会抛出一个引用错误,因为this并不是指foo 。 但是有没有办法让对象的值取决于其他属性?

那么,我可以告诉你的唯一的事情就是获得者:

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

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

你可以做这样的事情:

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

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

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

显然,简单的答案是缺失的,所以为了完整性:

但是有没有办法让对象的值取决于其他属性?

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

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

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

如果你需要这一切都在一个expression式,你可以做到这一点没有临时财产:

 var foo = function(o) { oc = oa + ob; return o; }({a: 5, b: 6}); 

或者当然,如果你不止一次需要这样做:

 function buildFoo(a, b) { var o = {a: a, b: b}; oc = oa + ob; return o; } 

那么你需要使用它的地方:

 var foo = buildFoo(5, 6); 

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

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

有些closures应该处理这个;

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

所有在foo中声明的variables都是foo的私有variables,就像你期望的任何函数声明一样,因为它们都在范围之内,它们都可以访问对方而不需要引用this ,就像你期望的那样。 不同之处在于这个函数返回一个暴露私有variables的对象,并将该对象赋值给foo 。 最后,你只要返回你想要公开的接口作为return {}语句的对象。

然后函数在最后执行() ,这将导致整个foo对象被评估,实例化的所有variables和作为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 } } } } 

通过将self定义为包含打印函数的对象,可以使函数引用该对象。 这意味着如果您需要将打印function传递到某个对象,则不必将其“绑定”到该对象。

如果你愿意的话,可以使用下面的例子

 function createMyObject() { var count = 0 return { a: { log: function() { console.log(count++) return this } } } } 

然后下面的代码将logging0,1,2,然后给出一个错误

 var o = createMyObject() var log = oalog oalog().log() // this refers to the oa object so the chaining works log().log() // this refers to the window object so the chaining fails! 

通过使用self方法,可以保证打印将始终返回相同的对象,而不pipe函数的运行环境如何。 上面的代码将运行得很好,并在使用createMyObject()的自我版本时logging0,1,2和3。

现在在ES6中,您可以创build懒惰的caching属性。 在第一次使用属性评估一次成为一个正常的静态属性。 结果:第二次跳过math函数开销。

魔法在吸气。

 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} 

你可以使用模块模式来做到这一点。 就像:

 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/

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

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

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

 function Obj() { this.a = 5; this.b = this.a + 1; // return this; // commented out because this happens automatically } var o = new Obj(); ob; // === 6 

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

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

在你的对象字面上创build新的函数并调用一个构造函数看起来与原来的问题是完全不同的,而且没有必要。

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

 var x = { a: 1, b: 2, c: a + b } // not defined var y = { a: 1, b: 2, c: ya + yb } // not defined 

计算属性的最简单的解决scheme如下(没有堆,没有函数,没有构造函数):

 var x = { a: 1, b: 2 }; xc = xa + xb; // apply computed property 

在这里张贴的其他答案更好,但是这里有一个替代scheme:

  • 设置初始化时的值(不是getter或派生等)
  • 对象字面值之外不需要任何types的init()或代码
  • 是一个对象字面值,而不是工厂函数或其他对象创build机制。
  • 不应该有任何性能影响(初始化除外)

自动执行匿名function和窗口存储

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

订单是保证 ( bar之前baz )。

它当然会污染window ,但是我无法想象有人在写一个需要window.temp的脚本。 也许tempMyApp如果你是偏执狂。

这也是丑陋的,但偶尔有用。 一个例子是当你使用一个刚性初始化条件的API,并且不想重构,所以这个范围是正确的。

当然是干的。

如果你的对象被写为一个返回对象的函数,并且你使用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); 

所有这一切的关键是范围

你需要封装你想要定义的属性的“parent”(父对象)作为自己的实例化对象,然后你可以使用关键字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) }; 

这个解决scheme怎么样,这将与数组的嵌套对象一起工作

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

我使用下面的代码作为替代,它的工作原理。 variables也可以是数组。 (@ 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]