Đặt giá trị mặc định của các thuộc tính đối tượng javascript


82

Có cách nào để đặt thuộc tính mặc định của một đối tượng javascript như vậy không:

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

IE có thể bị coi thường, Chrome Frame đã giúp tôi giải tỏa cơn đau đầu đó.


MỌI thuộc tính không tồn tại hay chỉ là một tên thuộc tính BIẾT?
js1568

Câu trả lời:


114

Kể từ khi tôi hỏi câu hỏi vài năm trước, mọi thứ đã tiến triển tốt.

Proxy là một phần của ES6. Ví dụ sau hoạt động trong Chrome, Firefox, Safari và Edge :

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

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

p.answerToTheUltimateQuestionOfLife; //=> 42

Đọc thêm trong tài liệu của Mozilla về Proxy .


Nếu bạn đang lên kế hoạch hỗ trợ Internet Explorer (trước Edge), bạn không gặp may rồi: caniuse.com/#search=proxy Ngoài ra, polyfill github.com/tvcutsem/harmony-reflect không hỗ trợ IE
Tiagojdferreira

21
Chúc mừng, bạn đã kiếm được Phoenix (Vàng Huy hiệu: trả lời một trong những câu hỏi riêng của bạn ít nhất một năm sau đó, với một câu trả lời rằng ít nhất gấp đôi số phiếu biểu quyết của câu trả lời phổ biến nhất)
Ziggy

Object.withDefault = (defaultValue, o = {}) => new Proxy (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

Vì vậy, việc sử dụng Proxy cũng có nghĩa là Object.getEntrieskhông thể gọi được trên Proxy :(
Meredith

1
Nếu bạn muốn nói Object.entries, bạn có thể sửa đổi trình xử lý để đặt thuộc tính khi chúng được truy cập. Thay đổi return target.hasOwnProperty(name) ? target[name] : 42;thành if (!target.hasOwnProperty(name)) target[name] = 42; return target[name];.
dosentmatter

28

Không có cách nào để đặt điều này trong Javascript - trả về undefinedcho các thuộc tính không tồn tại là một phần của đặc tả Javascript cốt lõi. Xem cuộc thảo luận cho câu hỏi tương tự này . Như tôi đã đề xuất ở đó, một cách tiếp cận (mặc dù tôi thực sự không thể đề xuất nó) sẽ là xác định một getPropertyhàm toàn cục :

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"

Nhưng điều này sẽ dẫn đến một loạt mã không chuẩn mà người khác sẽ khó đọc và nó có thể gây ra những hậu quả không mong muốn ở những khu vực mà bạn mong đợi hoặc muốn một giá trị không xác định. Tốt hơn là chỉ cần kiểm tra khi bạn đi:

var someVar = o.someVar || "my default";

10
cảnh báo: var someVar = o.someVar || "my default";sẽ có kết quả có thể không mong muốn khi o.someVar được điền nhưng đánh giá là false (ví dụ: null, 0, ""). someVar = o.someVar === undefined ? o.someVar : "my default";sẽ tốt hơn. Tôi thường sử dụng ||một mình khi mặc định cũng đánh giá là false. (e.g. o.someVar || 0 ')
Nhất trí

5
Đó là một điểm tốt - điều này sẽ không hoạt động ở bất kỳ nơi nào mà giá trị false-y là đầu vào hợp lệ và bạn phải cân nhắc điều đó khi sử dụng mẫu này. Nhưng thường xuyên hơn không, sẽ có ý nghĩa khi xử lý một thuộc tính được đặt rõ ràng thành nullhoặc falsetheo cách tương tự như một thuộc tính chưa được đặt, trong trường hợp này, điều này có nghĩa vụ kép. Tuy nhiên, lời cảnh báo là công bằng.
nrabinowitz

5
@Shanimal dụ của bạn là một cách sai lầm xung quanh, nó phải là someVar = o.someVar === undefined ? "my default" : o.someVar;, chỉ có một vấn đề nhỏ nhưng nó ném cho tôi một chút khi tôi lần đầu tiên thử code của bạn ;-)
Metalskin

vâng @Metalskin chúng tôi sẽ không quan tâm đến giá trị không xác định đúng không? cười lớn. xin lỗi, hy vọng sai lầm không khiến bạn mất quá nhiều thời gian :)
Shanimal

28

Sử dụng cấu trúc hủy (mới trong ES6)

tài liệu tuyệt vời của Mozila cũng như một bài đăng trên blog tuyệt vời giải thích cú pháp tốt hơn tôi có thể.

Để trả lời câu hỏi của bạn

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

Đi xa hơn

Tôi có thể đổi tên biến này không? Chắc chắn rồi!

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

Còn về dữ liệu lồng nhau? Mang nó vào!

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

Điều này chắc chắn nghe giống như cách sử dụng điển hình của các đối tượng dựa trên protoype:

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

nếu bạn gọi console.log (blankObj), nó sẽ trả về {}. không phải {thuộc
tính1

2
Có, vì attribute1: defaultValue1là trên nguyên mẫu và console.log chỉ liệt kê các mục được đặt trên đối tượng cấp cao nhất, không phải trên nguyên mẫu. Nhưng, giá trị ở đó khi tôi console.log(emptyObj.attribute1)thể hiện.
jfriend00

nó đúng nhưng nó cùng một vấn đề với JSON.stringify(emptyobj). Tôi đã buộc phải tạo một phương thức trả về tất cả các thuộc tính để phản hồi lại vấn đề này
thromrin19

11

mã của tôi là:

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
Tôi thích giải pháp này nhất, vì nó tương tự như những gì tôi làm trong PHP. function foo( array $kwargs = array() ) { // Fill in defaults for optional keyworded arguments. $kwargs += array( 'show_user' => true, 'show_links' => false, ); ...
Mike Finch

10

Cách tôi đạt được điều này là với object.assignhàm

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
Việc làm này bạn cũng được ghi đè lên các giá trị của overwritePropertiescách chính xác là:const newObj = Object.assign({}, defaultProperties, overwriteProperties)
Francisco Puga

6

Tôi nghĩ rằng cách tiếp cận đơn giản nhất đang sử dụng Object.assign.

Nếu bạn có Lớp học này:

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

Bạn có thể sử dụng nó như thế này:

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

Tài liệu (với polyfill): https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


5

Hoặc bạn có thể thử cái này

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

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

15
Xấu ý tưởng nếu dict [ 'anotherKey'] là 0.
Ray Toal

Có lẽ bạn nên xem xét điều này: codereadability.com/…
ebragaparah

@RayToal Sau đó, sẽ không String()sửa chữa điều đó? Như trong:val = dict[String('anotherkey')] || 'anotherval';
Godstime Osarobo

Không, xin lỗi, nó sẽ không. Cùng một vấn đề. Hãy thử nó: Đầu tiên làm d = {x: 1, y: 0}sau đó d['y'] || 100và không nó sai lầm cho 100. Sau đó thử ý tưởng của bạn đó là d[String('y')] || 100- nó vẫn đúng cho 100 khi nó sẽ cho 0.
Ray Toal

4

Đơn giản nhất trong tất cả các Giải pháp:

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

Hiện nay,

dict['last'] || 'Excluded'

sẽ trả về 'Bị loại trừ', là giá trị mặc định.


3
điều này không thành công nếu bạn có một mệnh lệnh hơi khác:dict = {'first': 0, 'second': 1, 'third': 2}
Vlad V

1
Đây cũng là một cách tuyệt vời để xâu chuỗi các giá trị có thể không tồn tại. Ví dụ a.b.c.d. Nếu a, d hoặc c không được xác định, bạn sẽ gặp lỗi, nhưng bạn có thể làm được(((a || {}).b || {}).c || {}).d) || "default"
hobberwickey

3

Đối với tôi, đây có vẻ là cách đơn giản và dễ đọc nhất để làm như vậy:

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

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

Tham chiếu Object.assign ()


Tôi sẽ trả lời rằng thư viện gạch dưới có phương thức _.defaults (đối tượng, * defaults) cho điều đó, nhưng câu trả lời của bạn vẫn giống nhau mà không có bất kỳ thư viện nào!
DaniCE

2

Tôi đã xem một bài báo hôm qua đề cập đến một Object.__noSuchMethod__ tính: JavascriptTips Tôi chưa có cơ hội chơi với nó, vì vậy tôi không biết về hỗ trợ trình duyệt, nhưng có thể bạn có thể sử dụng nó theo cách nào đó?


Cũng thấy trang đó, chắc chúng ta cũng đã từng đọc bài của HN. Chỉ các phương pháp. Nó có thể được thực hiện với defineGetter nhưng điều đó không có trong ECMA5 rất tiếc. Họ không có cách tiếp cận getter / setter khác tệ hơn theo quan điểm của tôi (yêu cầu xác định trước các thuộc tính).
sandstrom

Đây có thể là một câu trả lời trong tương lai :) Hãy trình duyệt hy vọng sẽ đạt được điều đó sớm :) ♬
jave.web

1
Tôi tin rằng câu trả lời trong tương lai sẽ là sử dụng Proxy
James Long

@JamesLong vâng, bạn chính xác! Hóa ra tương lai đã đến :) 3 năm sau khi tôi đặt câu hỏi, tính năng này giờ đã hoạt động trong FF (và các trình duyệt khác sớm). Tôi đã thêm một câu trả lời bên dưới.
sandstrom

2

Tôi ngạc nhiên là chưa ai đề cập đến toán tử bậc ba.

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

Bằng cách này, ngay cả khi giá trị của 'c' là 0, nó vẫn sẽ nhận được giá trị chính xác.


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

Vì vậy, ... sử dụng proxy cũng có nghĩa là bạn mất đi tất cả các phương pháp đẹp Object nay :(
Meredith

0

Điều này thực sự có thể làm với Object.create. Nó sẽ không hoạt động đối với các thuộc tính "không được xác định". Nhưng đối với những cái đã được cho một giá trị mặc định.

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

Sau đó, khi bạn tạo đối tượng thuộc tính, bạn sẽ làm điều đó với Object.create

properties = Object.create(defaults);

Bây giờ bạn sẽ có hai đối tượng trong đó đối tượng đầu tiên trống, nhưng nguyên mẫu trỏ đến defaultsđối tượng. Để kiểm tra:

console.log('Unchanged', properties);
properties.a = 'updated';
console.log('Updated', properties);
console.log('Defaults', Object.getPrototypeOf(properties));

0

Một cách tiếp cận sẽ là lấy một đối tượng mặc định và hợp nhất nó với đối tượng đích. Đối tượng đích sẽ ghi đè các giá trị trong đối tượng mặc định.

jQuery có .extend()phương thức thực hiện điều này. Tuy nhiên, jQuery là không cần thiết vì có các triển khai JS vani như có thể tìm thấy ở đây:

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


-1

Tôi đến đây để tìm giải pháp vì tiêu đề phù hợp với mô tả vấn đề của tôi nhưng nó không phải là thứ tôi đang tìm kiếm nhưng tôi đã có giải pháp cho vấn đề của mình (tôi muốn có một giá trị mặc định cho một thuộc tính sẽ là động như ngày ).

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

Đoạn mã trên là giải pháp mà cuối cùng tôi đã giải quyết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.