These articles are abridged from my book on Learning Apps Script and Office to Apps migration.
Going GAS, from 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' };
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
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.
Creating a prototype.
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
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
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.
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
function combineChain (ob,obCombined) { return ob ? combineChain( Object.getPrototypeOf(ob), Object.keys(ob).reduce(function(p,c) { if (!p.hasOwnProperty(c)) p = ob; 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.