Установить значение по умолчанию для атрибутов объекта javascript

83

Есть ли способ установить атрибут по умолчанию для объекта javascript таким образом, чтобы:

var emptyObj = {};
// do some magic
emptyObj.nonExistingAttribute // => defaultValue

IE можно не рассматривать, Chrome Frame избавил меня от этой головной боли.

Sandstrom
источник
КАЖДЫЙ несуществующий атрибут или только ИЗВЕСТНОЕ имя атрибута?
js1568 06

Ответы:

120

С тех пор, как я задал этот вопрос несколько лет назад, дела пошли хорошо.

Прокси являются частью ES6. Следующий пример работает в Chrome, Firefox, Safari и Edge :

var handler = {
  get: function(target, name) {
    return target.hasOwnProperty(name) ? target[name] : 42;
  }
};

var p = new Proxy({}, handler);

p.answerToTheUltimateQuestionOfLife; //=> 42

Подробнее читайте в документации Mozilla по прокси .

Sandstrom
источник
Если вы планируете поддерживать Internet Explorer (до Edge), вам не повезло: caniuse.com/#search=proxy Кроме того, polyfill github.com/tvcutsem/harmony-reflect не поддерживает IE
Tiagojdferreira
21
Поздравляю, вы заработали Феникс (Золотой значок: ответил на один из ваших вопросов по крайней мере год спустя, с ответом, который как минимум удвоил количество голосов за самый популярный ответ)
Зигги
Object.withDefault = (defaultValue, o = {}) => новый прокси (o, {get: (o, k) => (k in o)? O [k]: defaultValue}); o = Object.withDefault (42); ox // => 42 ox = 10 ox // => 10 o.xx // => 42
Vlad V
Таким образом, использование прокси также означает Object.getEntriesневозможность вызова прокси :(
Мередит
1
Если вы имеете в виду Object.entries, вы можете изменить обработчик, чтобы установить свойства при доступе к ним. Измените return target.hasOwnProperty(name) ? target[name] : 42;на if (!target.hasOwnProperty(name)) target[name] = 42; return target[name];.
dosentmatter
31

Используйте деструктурирование (новое в ES6)

Есть отличный документация от Mozila, а также фантастическая запись в блоге, которая объясняет синтаксис лучше, чем я.

Чтобы ответить на ваш вопрос

var emptyObj = {};
const { nonExistingAttribute = defaultValue } = emptyObj;
console.log(nonExistingAttribute); // defaultValue

Идти дальше

Могу я переименовать эту переменную? Конечно!

const { nonExistingAttribute: coolerName = 15} = emptyObj;
console.log(coolerName); // 15

А как насчет вложенных данных? Давай!

var nestedData = {
    name: 'Awesome Programmer',
    languages: [
        {
            name: 'javascript',
            proficiency: 4,
        }
    ],
    country: 'Canada',
};

var {name: realName, languages: [{name: languageName}]} = nestedData ;

console.log(realName); // Awesome Programmer
console.log(languageName); // javascript
Alexdriedger
источник
28

Невозможно установить это в Javascript - возврат undefinedнесуществующих свойств является частью основной спецификации Javascript. См. Обсуждение этого аналогичного вопроса . Как я предлагал там, один из подходов (хотя я не могу его рекомендовать) заключался бы в определении глобальной getPropertyфункции:

function getProperty(o, prop) {
    if (o[prop] !== undefined) return o[prop];
    else return "my default";
}

var o = {
    foo: 1
};

getProperty(o, 'foo'); // 1
getProperty(o, 'bar'); // "my default"

Но это приведет к появлению кучи нестандартного кода, который будет трудно читать другим, и может иметь непредвиденные последствия в тех областях, где вы ожидаете или хотите получить неопределенное значение. Лучше просто проверять по ходу:

var someVar = o.someVar || "my default";
Нрабинович
источник
10
предупреждение: var someVar = o.someVar || "my default";будет иметь потенциально неожиданные результаты, если o.someVar заполнен, но имеет значение false (например, null, 0, ""). someVar = o.someVar === undefined ? o.someVar : "my default";было бы лучше. Я обычно использую ||один, когда значение по умолчанию также false. (e.g. равно o.someVar || 0`)
Shanimal
5
Это хороший момент - это не сработает везде, где значение false-y является допустимым вводом, и вы должны учитывать это при использовании этого шаблона. Но чаще всего имеет смысл трактовать свойство, явно установленное на nullили falseтаким же образом, как и неустановленное свойство, и в этом случае это выполняет двойную функцию. Однако предупреждение справедливо.
nrabinowitz
5
@Shanimal Ваш пример неверен, так и должно быть someVar = o.someVar === undefined ? "my default" : o.someVar;, это всего лишь небольшая проблема, но меня это немного
поразило,
да @Metalskin, мы бы не заботились о неопределенном значении, не так ли? смешно. извините, надеюсь, ошибка не стоила вам слишком много времени :)
Shanimal
11

Это действительно похоже на типичное использование объектов на основе прототипов:

// define a new type of object
var foo = function() {};  

// define a default attribute and value that all objects of this type will have
foo.prototype.attribute1 = "defaultValue1";  

// create a new object of my type
var emptyObj = new foo();
console.log(emptyObj.attribute1);       // outputs defaultValue1
jfriend00
источник
если вы вызываете console.log (emptyObj), он возвращает {}. not {attribute1: 'defaultValue1'}
throrin19,
2
Да, потому что он attribute1: defaultValue1находится в прототипе, а console.log перечисляет только элементы, установленные в объекте верхнего уровня, а не в прототипе. Но ценность там, как я console.log(emptyObj.attribute1)показывает.
jfriend00
это правильно но это та же проблема с JSON.stringify(emptyobj). Я был вынужден создать метод, который возвращает все атрибуты в ответ на эту проблему
throrin19
11

мой код:

function(s){
    s = {
        top: s.top || 100,    // default value or s.top
        left: s.left || 300,  // default value or s.left
    }
    alert(s.top)
}
Киян
источник
1
Мне больше всего нравится это решение, потому что оно похоже на то, что я делаю в PHP. function foo( array $kwargs = array() ) { // Fill in defaults for optional keyworded arguments. $kwargs += array( 'show_user' => true, 'show_links' => false, ); ...
Майк Финч
10

Я добиваюсь этого с помощью object.assignфункции

const defaultProperties = { 'foo': 'bar', 'bar': 'foo' };
const overwriteProperties = { 'foo': 'foo' };
const newObj = Object.assign({}, defaultProperties, overwriteProperties);
console.log(defaultProperties);  // {"foo": "bar", "bar": "foo"}
console.log(overwriteProperties);  // { "foo": "foo" };
console.log(newObj);  // { "foo": "foo", "bar": "foo" }
Колин
источник
5
Делая это, вы также перезаписываете значения overwritePropertiesправильным способом:const newObj = Object.assign({}, defaultProperties, overwriteProperties)
Франсиско Пуга,
6

Я думаю, что самый простой подход - использовать Object.assign .

Если у вас есть этот класс:

class MyHelper {
    constructor(options) {
        this.options = Object.assign({
            name: "John",
            surname: "Doe",
            birthDate: "1980-08-08"
        }, options);
    }
}

Вы можете использовать это так:

let helper = new MyHelper({ name: "Mark" });
console.log(helper.options.surname); // this will output "Doe"

Документация (с полифилом): https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Gianlucaparadise
источник
6

Самое простое из всех решений:

dict = {'first': 1,
        'second': 2,
        'third': 3}

В настоящее время,

dict['last'] || 'Excluded'

вернет «Исключено», что является значением по умолчанию.

Гил Баджо
источник
3
это не удается, если у вас немного другой диктат:dict = {'first': 0, 'second': 1, 'third': 2}
Vlad V
2
Это также отличный способ связать ценности, которые могут не существовать. Например a.b.c.d. Если a, d или c не определены, вы (((a || {}).b || {}).c || {}).d) || "default"
столкнетесь
1
Лучшее решение на сегодняшний день!
Артур Хардуим
5

Или вы можете попробовать это

dict = {
 'somekey': 'somevalue'
};

val = dict['anotherkey'] || 'anotherval';
пользователь1386350
источник
15
Плохая идея, если dict ['anotherkey'] равно 0.
Ray Toal
Возможно, вам стоит подумать над этим: codereadability.com/…
ebragaparah
@RayToal Тогда бы это не String()исправить? Как в:val = dict[String('anotherkey')] || 'anotherval';
Godstime Osarobo
Нет, извините, это не так. Та же проблема. Попробуйте: во- первых делать d = {x: 1, y: 0}то d['y'] || 100и не он ошибочно дает 100. Затем попробуйте вашу идею что d[String('y')] || 100- она до сих пор неправильно дает 100 , когда он должен дать 0.
Рэй Toal
4

Мне кажется, это наиболее простой и читаемый способ:

let options = {name:"James"}
const default_options = {name:"John", surname:"Doe"}

options = Object.assign({}, default_options, options)

Ссылка на Object.assign ()

Хоакин Келлер
источник
Я собирался ответить, что в библиотеке подчеркивания есть метод _.defaults (object, * defaults) для этого, но ваш ответ будет таким же без какой-либо библиотеки!
DaniCE
2

Вчера я видел статью, в которой упоминается Object.__noSuchMethod__свойство: JavascriptTips. У меня не было возможности поиграть с ним, поэтому я не знаю о поддержке браузера, но, может быть, вы могли бы использовать это как-то?

Джеймс Лонг
источник
Видели и эту страницу, наверное, мы читали ту же статью HN. Только методы. Это можно сделать с помощью defineGetter, но, к сожалению, этого нет в ECMA5. У них не было другого подхода с получением / установкой, который, на мой взгляд, хуже (требует предварительного определения свойств).
Sandstrom 07
Это может быть ответом в будущем :) Будем надеяться, что браузеры скоро дойдут до этого :) ♬
jave.web
1
Я считаю, что в будущем ответом будет использование прокси-сервера
Джеймс Лонг
@JamesLong да, вы правы! оказывается, будущее наступило :) Через 3 года после того, как я задал вопрос, теперь это работает в FF (и скоро в других браузерах). Я добавил ответ ниже.
Sandstrom
2

Я удивлен, что никто еще не упомянул тернарный оператор.

var emptyObj = {a:'123', b:'234', c:0};
var defaultValue = 'defaultValue';
var attr = 'someNonExistAttribute';
emptyObj.hasOwnProperty(attr) ? emptyObj[attr] : defaultValue;//=> 'defaultValue'


attr = 'c'; // => 'c'
emptyObj.hasOwnProperty(attr) ? emptyObj[attr] : defaultValue; // => 0

Таким образом, даже если значение 'c' равно 0, оно все равно получит правильное значение.

Алан Донг
источник
1
Object.withDefault = (defaultValue,o={}) => {
  return new Proxy(o, {
    get: (o, k) => (k in o) ? o[k] : defaultValue 
  });
}

o = Object.withDefault(42);
o.x  //=> 42

o.x = 10
o.x  //=> 10
o.xx //=> 42
Влад В
источник
Итак ... использование прокси также означает, что вы сейчас теряете все хорошие методы Object :(
Мередит
0

На самом деле это возможно Object.create. Это не будет работать для «неопределенных» свойств. Но для тех, которым присвоено значение по умолчанию.

var defaults = {
    a: 'test1',
    b: 'test2'
};

Затем, когда вы создаете свой объект свойств, вы делаете это с помощью Object.create

properties = Object.create(defaults);

Теперь у вас будет два объекта, первый из которых пуст, но прототип указывает на defaultsобъект. Тестировать:

console.log('Unchanged', properties);
properties.a = 'updated';
console.log('Updated', properties);
console.log('Defaults', Object.getPrototypeOf(properties));
Нильс
источник
0

Один из подходов - взять объект по умолчанию и объединить его с целевым объектом. Целевой объект переопределит значения в объекте по умолчанию.

jQuery имеет .extend() метод, который это делает. Однако jQuery не нужен, так как есть ванильные реализации JS, такие как здесь:

http://gomakethings.com/vanilla-javascript-version-of-jquery-extend/

Дуг Амос
источник
-1

Я пришел сюда в поисках решения, потому что заголовок соответствовал описанию моей проблемы, но это не то, что я искал, но я получил решение своей проблемы (я хотел иметь значение по умолчанию для атрибута, который был бы динамическим, например, date ).

let Blog = {
title  : String,
image  : String,
body   : String,
created: {type: Date, default: Date.now}
}

Приведенный выше код был решением, на котором я наконец остановился.

Джибин Филиппос
источник