Встановити значення за замовчуванням для атрибутів об'єкта javascript


83

Чи є спосіб встановити атрибут за замовчуванням для об'єкта javascript таким чином, щоб:

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

IE можна знехтувати, Chrome Frame позбавив мене від цього головного болю.


КОЖНИЙ неіснуючий атрибут чи просто ВІДОМЕ ім’я атрибута?
js1568,

Відповіді:


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 про проксі .


Якщо ви плануєте підтримувати 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
Влад 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

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`

5
Це хороший момент - це не буде працювати ніде, де значення false-y є допустимим введенням, і ви повинні врахувати це, використовуючи цей шаблон. Але частіше за все має сенс ставитись до властивості, явно встановленої nullабо falseтаким самим чином, як до невстановленої властивості, і в цьому випадку це має подвійний обов'язок. Однак попередження справедливе.
nrabinowitz

5
@Shanimal Ваш приклад - неправильний шлях, він повинен бути someVar = o.someVar === undefined ? "my default" : o.someVar;, лише незначна проблема, але це мене трохи підкинуло, коли я вперше спробував ваш код ;-)
Metalskin

так @Metalskin, нам би було байдуже невизначене значення, чи не так? Лол. вибачте, сподіваюся, помилка не коштувала вам занадто багато часу :)
Шанімаль

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

якщо ви викликаєте 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, ); ...
Майк Фінч

А що, якщо 's' будуть невизначеними (тобто взагалі не вказані)?
Дідзіс

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)
Франсіско Пуга

7

Я думаю, що використовується найпростіший підхід 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


6

Найпростіші з усіх рішень:

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

Зараз,

dict['last'] || 'Excluded'

поверне "Виключено", що є значенням за замовчуванням.


3
це не вдається, якщо у вас дещо інший дикт:dict = {'first': 0, 'second': 1, 'third': 2}
Влад V,

2
Це також чудовий спосіб зв’язати цінності, які можуть не існувати. Наприклад a.b.c.d. Якщо a, d або c не визначені, ви потрапляєте в помилку, але ви можете просто зробити(((a || {}).b || {}).c || {}).d) || "default"
hobberwickey

1
Найкраще рішення на сьогоднішній день!
Arthur Harduim

5

Або ви можете спробувати це

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

val = dict['anotherkey'] || 'anotherval';

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.
Рей Тоал

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

Це може бути відповіддю в майбутньому :) Будемо сподіватися, що браузери незабаром туди потраплять :) ♬
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

Отже ... використання Proxies також означає, що ви програєте всі приємні методи 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

Я прийшов сюди шукати рішення, тому що заголовок відповідав моєму опису проблеми, але це не те, що я шукав, але я отримав рішення своєї проблеми (я хотів мати значення за замовчуванням для атрибута, який би був динамічним щось на зразок дати ).

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

Наведений вище код був рішенням, щодо якого я нарешті вирішив.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.