Самостоятельные ссылки в объектных литералах / инициализаторах - PullRequest
613 голосов
/ 06 января 2011

Есть ли способ заставить что-то вроде следующего работать в JavaScript?

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

В текущей форме этот код явно выдает ошибку ссылки, так как this не ссылается на foo.Но - это , есть ли способ, чтобы значения в свойствах литерала объекта зависели от других объявленных ранее свойств?

Ответы [ 23 ]

616 голосов
/ 06 января 2011

Ну, единственное, о чем я могу рассказать, это геттеры:

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

foo.c; // 11

Это синтаксическое расширение, представленное в спецификации ECMAScript 5-го издания, синтаксис поддерживается большинством современных браузеров (включая IE9).

292 голосов
/ 06 января 2011

Вы можете сделать что-то вроде:

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

Это будет некая единовременная инициализация объекта.

Обратите внимание, что вы фактически присваиваете возвращаемое значение init() для foo, поэтому вам необходимо return this.

163 голосов
/ 26 мая 2012

Очевидный, простой ответ отсутствует, поэтому для полноты:

Но - это , есть ли способ, чтобы значения в свойствах литерала объекта зависели от других свойств, объявленных ранее?

Нет. Все решения здесь откладывают его до тех пор, пока объект не будет создан (различными способами), а затем присваивают третье свойство. Самый простой способ - просто сделать это:

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

Все остальные - просто более косвенные способы сделать то же самое. (Феликс особенно умен, но требует создания и уничтожения временной функции, добавления сложности; либо оставляет дополнительное свойство объекта, либо [если вы 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);
60 голосов
/ 12 апреля 2013

Просто создайте анонимную функцию:

var foo = new function () {
    this.a = 5;
    this.b = 6;
    this.c = this.a + this.b;
};
21 голосов
/ 09 ноября 2016

Теперь в ES6 вы можете создавать ленивые кэшированные свойства.При первом использовании свойство оценивается один раз, чтобы стать нормальным статическим свойством.Результат: во второй раз пропускаются служебные данные математической функции.

Магия находится в получателе.

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

В получателе стрелы this поднимает окружающую лексическую область действия .

foo     // {a: 5, b: 6}
foo.c   // 11
foo     // {a: 5, b: 6 , c: 11}  
19 голосов
/ 02 июля 2013

Некоторое замыкание должно иметь дело с этим;

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

15 голосов
/ 25 мая 2013

Вы могли бы сделать это так

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

Определяя себя как объект, который содержит функцию печати, вы разрешаете функции ссылаться на этот объект. Это означает, что вам не нужно будет «связывать» функцию печати с объектом, если вам нужно передать его куда-то еще.

Если вместо этого вы используете 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().

8 голосов
/ 28 марта 2016

Для завершения, в ES6 у нас есть классы (поддерживаемые на момент написания этого только в последних браузерах, но доступные в Babel, TypeScript и других транспиляторах)

7 голосов
/ 08 января 2013

Вы можете сделать это, используя шаблон модуля. Прямо как:

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/

7 голосов
/ 06 января 2011

Есть несколько способов сделать это; вот что я бы использовал:

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
...