对象文字/初始化器中的自引用

kpo*_*zin 660 javascript object-literal

有没有办法在JavaScript中使用以下内容?

var foo = {
    a: 5,
    b: 6,
    c: this.a + this.b  // Doesn't work
};
Run Code Online (Sandbox Code Playgroud)

在当前形式中,此代码显然会抛出引用错误,因为this没有引用foo.但是,有什么办法对早些时候宣布的其他属性在对象文本的属性值依赖?

CMS*_*CMS 676

好吧,我唯一可以告诉你的是吸气剂:

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

console.log(foo.c) // 11
Run Code Online (Sandbox Code Playgroud)

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

  • 请注意,如果更改`foo.a`或`foo.b`的值,则使用此解决方案,那么`foo.c`的值也将同步更改.这可能是也可能不是. (47认同)
  • 非常有用的答案.有关'get'的更多信息,请访问:https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/get (31认同)
  • 请注意,`this`绑定到最深的嵌套对象.例如:`... x:{get c(){/*这是x,而不是foo*/}} ...` (6认同)
  • @FaustoR.是的,它确实. (5认同)
  • @HBP 这与问题中会发生的事情完全相同,所以在我看来,这正是预期的结果。 (3认同)
  • 为了完成我的上述声明,由于`foo`被声明为变量,并且`c`仅在调用时被求值,因此`c`内使用`foo`会起作用,而不是`this`(be小心点) (2认同)
  • @CertainPerformance是的,但是您需要先“删除”吸气剂(在吸气剂内),如[此答案](/sf/answers/2834880541/)中所示。需要注意的是,如果属性有 getter,则只能在删除 getter 或使用 Object.defineProperty 后才能更改其值。如果您使用自删除(即记忆)getter,如果该属性至少没有被访问过一次,则稍后的重新分配将会失败。 (2认同)

Fel*_*ing 303

你可以这样做:

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

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

请注意,你实际上是分配的返回值init()foo,所以你必须return this.

  • 你也可以在`return this`之前`删除this.init`,这样`foo`就不会被污染了 (93认同)
  • @BillyMoon:是的,尽管如此[影响性能](http://jsperf.com/test-dictionary-mode)对该对象的所有后续属性访问,在许多引擎上(例如V8). (15认同)
  • @MuhammadUmer:不确定ES6课程如何与问题相关. (8认同)
  • @MuhammadUmer:类只是构造函数的语法糖,所以它们并没有提供任何新的东西.无论哪种方式,这个问题的主要焦点是对象文字. (8认同)
  • @akantoword:很好:)因为对象文字是一个单独的表达式,所以`init()`调用直接附加在文字上以保持单个表达式.但是你当然可以单独调用你想要的功能. (3认同)
  • 您为什么必须最后退还此款项? (2认同)

T.J*_*der 167

缺少明显的简单答案,所以为了完整性:

但是,有什么办法对早些时候宣布的其他属性在对象文本的属性值依赖?

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

var foo = {
    a: 5,
    b: 6
};
foo.c = foo.a + foo.b;
Run Code Online (Sandbox Code Playgroud)

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

如果你需要它在一个表达式中,你可以在没有临时属性的情况下做到这一点:

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

或者当然,如果您需要多次执行此操作:

function buildFoo(a, b) {
    var o = {a: a, b: b};
    o.c = o.a + o.b;
    return o;
}
Run Code Online (Sandbox Code Playgroud)

然后你需要使用它:

var foo = buildFoo(5, 6);
Run Code Online (Sandbox Code Playgroud)


zzz*_*Bov 60

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

var foo = new function () {
    this.a = 5;
    this.b = 6;
    this.c = this.a + this.b;
};
Run Code Online (Sandbox Code Playgroud)

  • @zzzzBov:只需使用`var foo = function(){this....; 归还这个; } .call({});`这在语法上并没有太大的不同,但在语义上是理智的. (5认同)
  • 你有这个.我确实没有注意到`new`关键字. (3认同)

vos*_*usa 23

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

魔法在吸气剂中.

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

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

foo     // {a: 5, b: 6}
foo.c   // 11
foo     // {a: 5, b: 6 , c: 11}  
Run Code Online (Sandbox Code Playgroud)

  • 我也对“删除”感到摸不着头脑。我认为它所做的是从对象中删除“get c”属性并用标准属性覆盖它。我认为这样它只会计算一次,然后如果“a”或“b”稍后更改,“foo.c”将不会更新其值,但这也只能在调用“foo.c”时起作用/缓存/计算。 (2认同)

小智 20

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

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

    return {
        a: a,
        b: b,
        c: c
    }
}();
Run Code Online (Sandbox Code Playgroud)

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

然后在最后执行该函数,()其中导致整个foo对象被评估,实例化中的所有变量和作为属性添加的返回对象foo().

  • 把它称为"封闭"令人困惑和误导.虽然对于从函数中返回ojbect值的确切含义的意见不同,但并不构成任何人的书中的闭包. (13认同)

dav*_*ite 15

你可以这样做

var a, b
var foo = {
    a: a = 5,
    b: b = 6,
    c: a + b
}
Run Code Online (Sandbox Code Playgroud)

当我不得不引用最初声明函数的对象时,该方法对我有用.以下是我如何使用它的最小例子:

function createMyObject() {
    var count = 0, self
    return {
        a: self = {
            log: function() {
                console.log(count++)
                return self
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

通过将self定义为包含print函数的对象,您可以让函数引用该对象.这意味着如果您需要将打印功能传递给其他地方,则不必将打印功能"绑定"到对象上.

相反,如果您愿意,请this按照下图所示使用

function createMyObject() {
    var count = 0
    return {
        a: {
            log: function() {
                console.log(count++)
                return this
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,以下代码将记录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!
Run Code Online (Sandbox Code Playgroud)

通过使用self方法,您可以保证print始终返回相同的对象,而不管运行函数的上下文如何.上面的代码运行得很好,并在使用自我版本时记录0,1,2和3 createMyObject().

  • 很好的例子,除了你遗漏了所有的分号. (3认同)
  • 没有分号 - [It's](http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding) [很好](http://inimino.org/~inimino/ blog/javascript_semicolons) [真的!](https://www.youtube.com/watch?v=gsfbh17Ax9I) (2认同)

mon*_*onj 9

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

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

const foo = new Foo();
Run Code Online (Sandbox Code Playgroud)


Raf*_*cha 7

您可以使用模块模式执行此操作.就像:

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
Run Code Online (Sandbox Code Playgroud)

使用此模式,您可以根据需要实例化几个foo对象.

http://jsfiddle.net/jPNxY/1/

  • 这不是模块模式的示例,只是一个函数.如果foo定义的最后一行是`}();`,它将自行执行并返回一个对象,而不是一个函数.另外,`foo.c`是一个函数,因此写入函数和通过`fooObject.c()`的下一次调用的clobbers将失败.也许这个[小提琴](http://jsfiddle.net/awilliams47/3rbks/1/)更接近你想要的东西(它也是一个单例,不是为了实例化而设计的). (2认同)
  • "模块模式最初定义为为传统软件工程中的类提供私有和公共封装的方法".来自:[学习JavaScript设计模式](http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript).这个对象遵循上面描述的模块模式,但也许它不是最好的解释,因为没有显示公共和私有属性/方法.这个http://jsfiddle.net/9nnR5/2/与公共和私有属性/方法是同一个对象.所以他们都遵循这种模式 (2认同)

ken*_*ken 6

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

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
Run Code Online (Sandbox Code Playgroud)

  • 这有效,但消除了对象文字符号的优点. (2认同)

ani*_*ija 6

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

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

console.log(foo.c())
Run Code Online (Sandbox Code Playgroud)

上面也有更好的答案.这就是我修改你质疑的示例代码的方法.

更新:

var foo = {
    get a(){return 5},
    get b(){return 6},
    get c(){return this.a + this.b}
}
// console.log(foo.c);
Run Code Online (Sandbox Code Playgroud)

  • 在ES6中,您可以使这种通用方法更加优雅:`var foo = {get a(){return 5},get b(){return 6},get c(){return this.a + this.b}} `所以现在你可以做`foo.c`而不是`foo.c()`:)(随意把它粘贴到你的答案中,这样格式化就更好了!) (2认同)

Ant*_*ris 6

get属性效果很好,您还可以对“昂贵”的函数使用绑定闭包,该函数只应运行一次(这只适用于var,不适用于constor let

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)
})()
Run Code Online (Sandbox Code Playgroud)


小智 6

仅供大家娱乐:

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

console.log(foo);
Run Code Online (Sandbox Code Playgroud)

  • 哈哈 :-)........... (4认同)

Ric*_*hea 5

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

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

var x = { a: 1, b: 2, c: a + b } // not defined 
var y = { a: 1, b: 2, c: y.a + y.b } // not defined 
Run Code Online (Sandbox Code Playgroud)

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

var x = { a: 1, b: 2 };

x.c = x.a + x.b; // apply computed property
Run Code Online (Sandbox Code Playgroud)