对象字面声明中的自引用
有没有什么办法让下面的东西在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对象。
只是为了思考 – 将对象的属性放在时间轴之外:
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]