Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy object with results of getters

I have an object that contains a getter.

myObject {
    id: "MyId",
    get title () { return myRepository.title; }
}

myRepository.title = "MyTitle";

I want to obtain an object like:

myResult = {
    id: "MyId", 
    title: "MyTitle"
}

I don't want to copy the getter, so:

myResult.title;                       // Returns "MyTitle"
myRepository.title = "Another title";
myResult.title;                       // Should still return "MyTitle"

I've try:

  • $.extend(): But it doesn't iterate over getters. http://bugs.jquery.com/ticket/6145
  • Iterating properties as suggested here, but it doesn't iterate over getters.
  • As I'm using angular, using Angular.forEach, as suggested here. But I only get properties and not getters.

Any idea? Thx!

Update I was setting the getter using Object.defineProperty as:

 "title": { get: function () { return myRepository.title; }},

As can be read in the doc:

enumerable true if and only if this property shows up during enumeration of the properties on the corresponding object. Defaults to false.

Setting enumerable: true fix the problem.

"title": { get: function () { return myRepository.title; }, enumerable: true },
like image 966
Mario Levrero Avatar asked Dec 09 '25 07:12

Mario Levrero


2 Answers

$.extend does exactly what you want. (Update: You've since said you want non-enumerable properties as well, so it doesn't do what you want; see the second part of this answer below, but I'll leave the first bit for others.) The bug isn't saying that the resulting object won't have a title property, it's saying that the resulting object's title property won't be a getter, which is perfect for what you said you wanted.

Example with correct getter syntax:

// The myRepository object
const myRepository = { title: "MyTitle" };

// The object with a getter
const myObject = {
    id: "MyId",
    get title() { return myRepository.title; }
};

// The copy with a plain property
const copy = $.extend({}, myObject);

// View the copy (although actually, the result would look
// the same either way)
console.log(JSON.stringify(copy));

// Prove that the copy's `title` really is just a plain property:
console.log("Before: copy.title = " + copy.title);
copy.title = "foo";
console.log("After:  copy.title = " + copy.title);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Syntax fixes:

  • Added missing variable declarations, =, and ;

  • Removed duplicate property title

  • Corrected the getter declaration syntax


If you want to include non-enumerable properties, you'll need to use Object.getOwnPropertyNames because they won't show up in a for-in loop, Object.keys, or $.extend (whether or not they're "getter" or normal properties):

// The myRepository object
const myRepository = { title: "MyTitle" };

// The object with a getter
const myObject = {
    id: "MyId",
};
Object.defineProperty(myObject, "title", {
    enumerable: false, // it's the default, this is just for emphasis,
    get: function () {
        return myRepository.title;
    },
});

console.log("$.extend won't visit non-enumerable properties, so we only get id here:");
console.log(JSON.stringify($.extend({}, myObject)));

// Copy it
const copy = {};
for (const name of Object.getOwnPropertyNames(myObject)) {
    copy[name] = myObject[name];
}

// View the copy (although actually, the result would look
// the same either way)
console.log("Our copy operation with Object.getOwnPropertyNames does, though:");
console.log(JSON.stringify(copy));

// Prove that the copy's `title` really is just a plain property:
console.log("Before: copy.title = " + copy.title);
copy.title = "foo";
console.log("After:  copy.title = " + copy.title);
.as-console-wrapper {
    max-height: 100% !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
like image 158
T.J. Crowder Avatar answered Dec 11 '25 19:12

T.J. Crowder


First of all, fix your syntax, though it probably is good in your actual code:

myObject = {
    id: "MyId",
    get title () { return myRepository.title; }
}

Now, to the answer. :)

You can just use a for..in loop to get all the properties, then save them as-is:

var newObj = {};
for (var i in myObject) {
    newObj[i] = myObject[i];
}

No jQuery, Angular, any other plugins needed!

like image 28
Scimonster Avatar answered Dec 11 '25 21:12

Scimonster



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!