If you have found this post while searching to try to figure out why your JavaScript Object.create() method is being a big stupid head isn't working how you would expect it would in Google Apps Script, you have come to the right place.
You've probably used Object.create()
in a JavaScript project in the past to create a new object based on a template object and then added another property or so to it.
const a = {name:"Yagi", species:"goat"} //In Javascript it works! const newObj = Object.create(a); newObj.foodAvailable = 100 console.log("a: ", a); //result >> "a: ", { //name: "Yagi", //species: "goat" //} console.log("newObj: ", newObj); //result>> "newObj: ", { //foodAvailable: 100, //name: "Yagi", //species: "goat" //}
As you can see in the example above, it works just dandy in JavaScript. The original object "a"
is not affected if we add a new property to the "newObj"
object.
However, if you try and do the same thing in Google Apps Script, as you have no doubt discovered, you will get the following results:
function appsScriptWhyHaveYouForsakenMe(){ const a = {name:"Yagi", species:"goat"} //Error const newObj = Object.create(a); newObj.foodAvailable = 100 console.log("a: ", a); //a: { name: 'Yagi', species: 'goat' } console.log("newObj: ", newObj); // newObj: { foodAvailable: 100 } }
So what's going on? What can I do to fix this? Read on or select from the menu to get to the bits you are interested in.
Some Solutions
Spread Syntax to the rescue
With the modernisation of Google Apps Script with their introduction of the V8 runtime back in February of 2020, we can now use the JavaScript Spread Syntax ({...obj}
)to create a shallow clone of the original object (a.k.a object literal).
This is my favourite approach. It is quite neat and elegant, but perhaps not as explicit in its meaning for someone reviewing your code.
If you create a new object and add a property to it
Dam it! Now I've got that Beyonce song in my head. *
Let's fix the original code with our spread syntax.
function ohohohhh(){ const a = {name:"Yagi", species:"goat"}; //Success const newObj = {...a}; newObj.foodAvailable = 100; console.log("a: ", a); // a: { name: 'Yagi', species: 'goat' } console.log("newObj: ", newObj); // newObj: { name: 'Yagi', species: 'goat', foodAvailable: 100 } };
You can see on line 5 we have a nice neat spread syntax to add to your "newObj"
object. This shallow clone of "a"
allows us to keep the original object unchanged while importing the properties into the new object.
Just like on line 6
, we can now add new properties to our new object without fear of updating the original source object, "a"
.
*Just when you thought the humour couldn't set a lower standard, the goat's gone subterranean.
Joining two objects together in Google Apps Script
Probably the most useful part of using the spread operator is that we can join or concatenate two objects together.
In our next example, we will join objects "a"
and "b"
together to form one new object with our spread syntax. To do this we simply separate the two objects by a comma and use the spread syntax on both.
function wontYouJoinMe(){ const a = {name:"Yagi", species:"goat"} const b = {hobby:"coding", friendly:true, action: ()=>{return 1 + 1}} const c = {...a,...b}; console.log("a: ", a); // a: { name: 'Yagi', species: 'goat' } console.log("b: ", b); // b: { hobby: 'coding', friendly: true, action: [Function: action] } console.log("c: ", c); // c: { // name: 'Yagi', // species: 'goat', // hobby: 'coding', // friendly: true, // action: [Function: action] } }
As you can see, "c"
has now combined "a"
and "b"
successfully while both "a"
and "b"
still maintain their individuality.
Adding an object to an existing object in Google Apps Script
One final thing you might find useful is to concatenate one or more objects to an existing object. We can do this with the Object. assign() method. This method takes a target object as its first parameter and then any number of objects as its subsequent parameters.
Adding one object to another.
Let's say if I wanted to add all of "a"
's properties to "b"
. It would look a little something like this:
function youWillBeAssimiated(){ const a = {name:"Yagi", species:"goat"} const b = {hobby:"coding", friendly:true, action: ()=>{return 1 + 1}} Object.assign(b, a) b.name = 'collective' console.log("a: ", a); // a: { name: 'Yagi', species: 'goat' } console.log("b: ", b); // b: { // hobby: 'coding', // friendly: true, // action: [Function: action], // name: 'Yagi', // species: 'goat' } }
As you can see, object "b"
now has the properties of "a"
, but if we change the "name"
property of "b"
to something else, then "a"
will not be affected.
Adding multiple objects to an existing object.
Likewise, we can add multiple objects to an existing object with the Object.assign()
method. In the next example, we will make a copy of "a"
, "b"
and "c"
objects and join it to object "d"
.
function weAreTheBorg(){ const a = {name:"All the single ladies", species:5618}; const b = {hobby:"singing", friendly:true, action: ()=>{return 1 + 1}}; const c = {leader:"Beyonce",title:"queen"}; const d = {cube: 1184, compliment:3155}; Object.assign(d, a, b, c) console.log("a: ", a); // a: { name: 'Yagi', species: 'goat' } console.log("b: ", b); // b: { hobby: 'singing', friendly: true, action: [Function: action] } console.log("c: ", c); // c: { leader: 'Beyonce', title: 'queen' } console.log("d: ", d); // d: { // cube: 1184, // compliment: 3155, // name: 'All the single ladies', // species: 5618, // hobby: 'singing', // friendly: true, // action: [Function: action], // leader: 'Beyonce', // title: 'queen' } };
As you have probably guessed by now, objects, "a"
,"b"
and "c"
are not affected by the Object.assign()
but now "d"
has assimilated a copy of the properties of the other objects.
Looking to learn more about Google Apps Scripts in a more structured format? Udemy has some great courses that can get you from the basics to a real Google Apps Script pro!
Got a more specific problem you need help with, but don't have the time to develop the skills? Make an enquiry on my 'Hire me!' page. I occasionally pick up projects. Alternatively, Fiverr's your best bet to find a skilled Google Apps Script developer to solve your problem quickly and professionally. *
*The above affiliate links have been carefully researched to get you to what you specifically need. If you decide to click on one of these links it will cost you just the same as going to the site. If you decide to sign up, I just get a little pocket money to help pay for the costs of running this website.
What's going on?
Well, I found a relatively obscure reference to the reason why Object.create() performs how we expect it to in StackOverflow that was answered by a former legendary member of the Google Apps Script team, Corey Goldfeder.
Corey informs us that in Google Apps Script when objects are passed to Object.create()
method, the properties of the original object are non-enumerable by default. This means that they cannot be explicitly iterated through with a for...in loop inside the create()
method.
Corey goes on to say that Object.create()
uses the Object.defineProperties() syntax which, unless the enumerable property is explicitly defined previously, will be defined as non-enumerable and not be added to the newly created object.
You could resolve or test this by explicitly defining the enumerability of each property in "a"
as true
which would then give you the result you were expecting.
function objectCreateExplicitEnumeration(){ const a = {name:{value:"Yagi", enumerable:true}, species:{value:"goat", enumerable:true}} //This will work, but it is yucky. const newObj = Object.create({},a); newObj.foodAvailable = 100 console.log("a: ", a); //a: { name: { value: 'Yagi', enumerable: true }, // species: { value: 'goat', enumerable: true } } console.log("newObj: ", newObj); // newObj: { name: 'Yagi', species: 'goat', foodAvailable: 100 } }
Note the changes to the object 'a'
and the parameters in the create
method (Line 6).
That's it for creating new objects based on existing objects in Google Apps Script. I would love to know how you used this code or if you have another approach. Feel free to share your ideas or questions in the comments below.
If you found the tutorial, useful why not shout me a cuppa.
~Yagi
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.