设置javascript对象属性的默认值


82

有没有一种方法可以设置javascript对象的默认属性,例如:

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

IE可以忽略,Chrome Frame减轻了我的头痛。


每个不存在的属性还是仅一个已知的属性名称?
js1568

Answers:


114

自从几年前我问这个问题以来,事情进展顺利。

代理是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的Proxies文档中了解更多信息。


如果您打算支持Internet Explorer(在Edge之前),那么您不走运:caniuse.com/#search=proxy 另外,polyfill github.com/tvcutsem/harmony-reflect不支持IE
Tiagojdferreira

21
恭喜,您赢得了Phoenix(金徽章:至少在一年后回答了您自己的问题之一,答案至少使最受欢迎的答案的投票数量翻了一番)
Ziggy

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无法在代理服务器上调用:(
Meredith

1
如果您的意思是Object.entries,则可以修改处理程序以在访问属性时设置属性。更改return target.hasOwnProperty(name) ? target[name] : 42;if (!target.hasOwnProperty(name)) target[name] = 42; return target[name];
剂量ntmatter

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值是有效输入的任何地方都无法使用,并且在使用此模式时必须考虑到这一点。但通常,将显式设置为未设置属性nullfalse与未设置属性相同的属性视为有意义,在这种情况下,这具有双重职责。但是,警告是公平的。
nrabinowitz 2012年

5
@Shanimal您的示例应该是错误的方法,应该someVar = o.someVar === undefined ? "my default" : o.someVar;只是一个小问题,但是当我第一次尝试使用您的代码时,它使我有点
烦恼

是的@Metalskin我们将不在乎未定义的值,对吧?大声笑。抱歉,希望该错误不会花费您太多时间:)
Shanimal

28

使用解构(新中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

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),它将返回{}。不是{attribute1:'defaultValue1'}
throrin19

2
是的,因为attribute1: defaultValue1是在原型和console.log上,它只枚举在顶级对象上设置的项目,而不是在原型上。但是,价值如我所console.log(emptyObj.attribute1)展示。
jfriend00 2013年

没错,但这与相同的问题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, ); ...
Mike Finch

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)
Francisco Puga

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"

文档(带有polyfill):https : //developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


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

所有解决方案中最简单的:

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

现在,

dict['last'] || 'Excluded'

将返回“已排除”,这是默认值。


3
如果您的字典稍有不同,则此操作将失败:dict = {'first': 0, 'second': 1, 'third': 2}
Vlad V

1
这也是链接可能不存在的值的好方法。例如a.b.c.d。如果未定义a,d或c,则您会遇到错误,但您可以这样做(((a || {}).b || {}).c || {}).d) || "default"
hobberwickey

3

在我看来,这是最简单易读的方式:

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 2011年

这可能是将来的答案:)让我们希望浏览器能尽快到达那里:)♬
jave.web 2015年

1
我相信未来的答案将是使用代理服务器
詹姆斯龙

@JamesLong是的,你是对的!事实证明,未来已经到来了:) 3年后,我问了这个问题,现在它可以在FF(和其他浏览器)中使用了。我在下面添加了答案。
sandstrom 2015年

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方法:(
Meredith

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));


-1

我来这里是为了寻找解决方案,因为标题与我的问题描述相匹配,但这不是我想要的,但是我为我的问题找到了解决方案(我想为属性设置一个默认值,该属性可以是动态的,例如date )。

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

上面的代码是我最终解决的解决方案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.