Using Object.create in Apps Script



These articles are abridged from my  book on Learning Apps Script and Office to Apps migration. 

Going GASfrom VBA to Google Apps Script.

Available in ebook or print from O'Reilly or Amazon or any other good bookshop.

ECMAScript 5 introduced a new way of creating objects, giving better control over properties and bringing some standardization. Apps Script is based on ECMAScript 3, but it also has some things from ES5, including Object.create. 

Here's how to use it.


Usual way to create objects.


We are all used to creating objects like this,
 
var mammal =  {
    livesOnLand:true,
    legs:4,
    warmBlooded:true,
    vertebrate:true,
    hair:true,
    layEggs:false,
    milk:true,
    kind:'mammal'
  };

or by using a constructor, along with the new keyword.

function Mammal ()   {

    this.livesOnLand = true;
    this.legs = 4;
    this.warmBlooded = true;
    this.vertebrate = true;
    this.hair = true;
    this.layEggs = false;
    this.milk = true;
    this.kind = 'mammal';

  };
 var mammal = new Mammal();

or modifying the function prototype of the constructor

function Mammal () {
}

Mammal.prototype.legs = 4;
// etc...

var mammal = new Mammal();

Another technique is to base one constructor on another

function SeaMammal () {
    Mammal.call (this);
    this.legs =0;
    this.livesOnLand = false;
}
 var seaMammal = new SeaMammal();

and so on. There are many different techniques, some more complicated than others.

Using Object.create


An object can be created like this
var ob = Object.create (thePrototype , { specific properties });

The specific properties object has all the capabilities of Object.defineProperties such as enumerability, writability and getters and setters, and is used to create properties specific to the object being created. 

thePrototype refers to the object to use as a protoype, and a null value will use the default, the  Object prototype.

Creating a prototype.

It's fairly easy to see how a standalone object can be created, but you can also use this to create objects which use other objects as prototypes. In this example, I'll create a prototype containing a couple of properties that are shared amongst all mammals, along with a getter property to summarize the object, and a build function that I'll use in the place of a constructor and the new keyword.

 var Mammal =  Object.create (null, {

    warmBlooded:{
      value:true,
      enumerable:true
    },
    
    vertebrate:{
      value:true,
      enumerable:true
    },
    
    introduction:{
      get:function () {
        return 'Im a kind of ' + this.kind + 
          ' called a ' + this.name + ' and I eat ' + this.eats +
          '. I' + (this.livesOnLand ? '' : " don't" ) + 
          ' live on land and I have ' + this.legs + ' limbs.'; 
      }
    },
    
    build: {
      value: function (name, eats) {
        this.name = name;
        this.eats = eats;
        return this;
      }
    }
    
  });

Stringifying this object gives this.
 Logger.log (JSON.stringify(Mammal));
{"warmBlooded":true,"vertebrate":true}


Here's another based on Mammal as a prototype, but with additional properties specific to a land Mammal.
  var LandMammal = Object.create (Mammal, {
    livesOnLand:{
      value: true, 
      enumerable:true
    },
    legs:{
      value:4,
      enumerable:true
    },
    kind:{
      value:'land mammal',
      enumerable:true
    }
  });

which stringifies to this
 Logger.log (JSON.stringify(LandMammal));
 {"livesOnLand":true,"legs":4,"kind":"land mammal"}

And another, with properties specific to a sea mammal
  var SeaMammal = Object.create (Mammal, {
    livesOnLand:{
      value: false, 
      enumerable:true
    },
    legs:{
      value:0,
      enumerable:true
    },
    kind:{
      value:'sea mammal',
      enumerable:true
    }
  });

which stringifies to this
 Logger.log (JSON.stringify(SeaMammal));
 {"livesOnLand":false,"legs":0,"kind":"sea mammal"}

Creating object instances

Animal specific objects can now be created with the two Prototype chains  (SeaMammal -> Mammal - > Object) and (LandMammal -> Mammal -> Object), 

  var dog = Object.create(LandMammal).build ('dog','meat');
  var rat = Object.create(LandMammal).build ('rat','anything');

You'll notice that the build function in the Mammal prototype dispenses with the need for any kind of constructor function (or the use of the new keyword) with any of these objects. Even though the build function is defined at the top of the prototype chain in the Mammal object, the animal specific properties belong to the animal object, as stringified below.

Logger.log (JSON.stringify(rat));
{"name":"rat","eats":"anything"}


In the same way, animal specific objects using the SeaMammal as a prototype can be created like this. 
  var whale = Object.create(SeaMammal).build ('whale', 'krill');
  var dolphin = Object.create(SeaMammal).build('dolphin','fish');

and produce this kind of stringification
 Logger.log (JSON.stringify(whale));
 {"name":"whale","eats":"krill"}


Using getters


When getters or setters are defined, it is not necessary to treat them as a function. This means that complicated things can be returned as properties without needing to know it's a function. In Apps script, getSomething() and setSomething() are the standard naming convention and also signal that these are functions. 

Using getters and setters with ES5, means that you treat everything as if it were a property. 

The introduction getter in the Mammal Object is called like this, even though it is actually a function behind the scenes, as in the examples below.

Logger.log(dolphin.introduction);
Im a kind of sea mammal called a dolphin and I eat fish. I don't live on land and I have 0 limbs.

Logger.log(dog.introduction);
Im a kind of land mammal called a dog and I eat meat. I live on land and I have 4 limbs.


Stringifying the prototype chain.


When the prototype chain is searched for a property it keeps going higher up the chain until it finds it, or runs out of prototypes. You'll have noticed that when stringifying the objects created above, only the properties of the current object are enumerable and visible. 

Object.getPrototypeOf can be used to return the prototype of an object, and it can be of course be stringified too. Here's how to get the stringified results of all each of the members of the prototype chain for our animal specific object.

  Logger.log (JSON.stringify(whale));
 {"name":"whale","eats":"krill"}

  Logger.log (JSON.stringify(Object.getPrototypeOf(whale)));
 {"livesOnLand":false,"legs":0,"kind":"sea mammal"}

  Logger.log (JSON.stringify(Object.getPrototypeOf(Object.getPrototypeOf(whale))));
 {"warmBlooded":true,"vertebrate":true}

Generalizing prototype chain stringification

You may want to combine all the enumerable properties in the prototype chain. It's pretty straightforward to do that recursively. Here's a function that can be used to combine all properties of a prototype chain. If there are duplicates the lower ones take precedence - so this pretty much emulates what traversing a prototype chain would look like internally to JavaScript. 

function combineChain (ob,obCombined) {
  return ob ?
    combineChain(
      Object.getPrototypeOf(ob),
      Object.keys(ob).reduce(function(p,c) {
        if (!p.hasOwnProperty(c)) p[c] = ob[c];
        return p;
      }, obCombined || {})) : obCombined;
}

and it can be used like this

Logger.log (JSON.stringify(combineChain(whale)));

{"name":"whale","eats":"krill","livesOnLand":false,"legs":0,"kind":"sea mammal","warmBlooded":true,"vertebrate":true}

I really like this way of dealing with inheritance and object creation. It's nice to see Apps Script moving forward. 

For more like this, see Google Apps Scripts snippets. Why not join our forumfollow the blog or follow me on twitter to ensure you get updates when they are available. 

You want to learn Google Apps Script?

Learning Apps Script, (and transitioning from VBA) are covered comprehensively in my my book, Going Gas - from VBA to Apps script, available All formats are available now from O'Reilly,Amazon and all good bookshops. You can also read a preview on O'Reilly

If you prefer Video style learning I also have two courses available. also published by O'Reilly.
Google Apps Script for Developers and Google Apps Script for Beginners.




Comments