Cazineer
Cazineer

Reputation: 2405

How to add prototypal inheritance to generated objects

I'm learning about prototypal inheritance and I've read numerous articles and watched multiple videos on the subject. It's starting to make sense now.

The following code example, which can be found at this JSFiddle, takes an array of objects and for each object, it sets a key in a Map object, with an object as its value.

The value object has two methods, get and set, which get and set values to another map object from the local scope of the template factory that returns the object.

My understanding is if we used this example to hypothetically generate 100k objects in the databaseMap, each object would have its own set and get methods, using unnecessary amounts of memory? My understanding is this is where I should be using prototypal inheritance?

So my question is, how do I move the set and get methods to say the scope of the init function and set them to the prototype of the object returned from template, so that when called, they set and get on the storeMap Map, from within the scope of the template function that generated the object?

Or, am I not understanding this correctly?

const config = [
    {
        id: 'obj1',
    value: 'value1',
    },
  {
        id: 'obj2',
    value: 'value2',
    },
  {
        id: 'obj3',
    value: 'value3',
    },
  {
        id: 'obj4',
    value: 'value4',
    },
]

function init() {
    const databaseMap = new Map()

  function template(storeConfig) {
    const { id } = storeConfig
    const storeMap = new Map()
    return {
      id,
      set(key, value) {
        console.log(`Setting data to store ${id}.`)
        // Do future work here
        return storeMap.set(key, value) 
      },
      get(key) {
        // Do future work here
        return storeMap.get(key)
      },      
    }
  }

  config.forEach(x => {
    const store = template({ id: x.id })
    databaseMap.set(x.id, store)
  })

  return databaseMap
}
const db = init()
const getStore = db.get('obj4')
getStore.set('testing1', 'testing1')
console.log('GET STORE')
console.log(getStore)
console.log('GET TESTING 1')
console.log(getStore.get('testing1'))

Upvotes: 2

Views: 55

Answers (1)

Mark
Mark

Reputation: 92440

Have you considered just using a single object with get and set defined on it? Instead of creating a new object your can create a new Template() that — then you can take advantage of the way this works in javascript to allow it to work for all instances while avoiding having to capture a new closure for each function.

For example:

const config = [{id: 'obj1', value: 'value1'}, {id: 'obj2', value: 'value2'},{id: 'obj3',value: 'value3',},{id: 'obj4',value: 'value4',}]

function init() {
    const databaseMap = new Map()
    // a single proto object
    const protoObj = {
        set(key, value) {
            console.log(`Setting data to store ${this.id}.`)
            // Do future work here
            return this.storeMap.set(key, value) 
        },
        get(key) {
            return this.storeMap.get(key)
        }
    }
    function Template(storeConfig) {
        this.id = storeConfig.id
        this.storeMap = new Map()
    }
    // use the object for the prototype
    Template.prototype = protoObj

    config.forEach(x => {
        const store = new Template({ id: x.id })
        databaseMap.set(x.id, store)
    })
  return databaseMap
}

const db = init()
const getStore = db.get('obj4')
getStore.set('aTest', 'testing1')
console.log('GET STORE')
console.log(getStore)
console.log('GET aTest')
console.log(getStore.get('aTest'))

Of course you can also define the function directly on the prototype:

Template.prototype.set = function (key, value) {
    return this.storeMap.set(key, value) 
}
Template.prototype.get = function (key, value) {
    return this.storeMap.get(key, value) 
}

EDIT based on comment

You can define the functions on their own in such a way that they use this to access the object's properties. Then you can just add a reference from them to the object. Like:

const config = [{id: 'obj1', value: 'value1'}, {id: 'obj2', value: 'value2'},{id: 'obj3',value: 'value3',},{id: 'obj4',value: 'value4',}]

function init() {
    const databaseMap = new Map()
    function set(key, value) {
        console.log(`Setting data to store ${this.id}.`)
        // Do future work here
        return this.storeMap.set(key, value) 
    }
    function get(key) {
        // Do future work here
        return this.storeMap.get(key)
    }
    function template(storeConfig) {
        return {
            id: storeConfig.id,
            storeMap:new Map(),
            set:set,
            get:get
        }
    }

    config.forEach(x => {
        const store = template({ id: x.id })
        databaseMap.set(x.id, store)
    })

    return databaseMap
}
const db = init()
const getStore = db.get('obj4')
getStore.set('TestKey', 'testing1')
console.log('GET STORE')
console.log(getStore)
console.log('TestKey')
console.log(getStore.get('TestKey'))

ALTERNATIVE

    const config = [{id: 'obj1', value: 'value1'}, {id: 'obj2', value: 'value2'},{id: 'obj3',value: 'value3',},{id: 'obj4',value: 'value4',}]
    
    function init() {
        const databaseMap = new Map()
        // a single proto object
        const protoObj = {
            set(key, value) {
                console.log(`Setting data to store ${this.id}.`)
                // Do future work here
                return this.storeMap.set(key, value) 
            },
            get(key) {
                return this.storeMap.get(key)
            }
        }
        function template(storeConfig) {
            const id = storeConfig.id
            const storeMap = new Map()
            return Object.assign(Object.create(protoObj), { id, storeMap })
        }
    
        config.forEach(x => {
            const store = template({ id: x.id })
            databaseMap.set(x.id, store)
        })
      return databaseMap
    }
    
    const db = init()
    const getStore = db.get('obj4')
    getStore.set('aTest', 'testing1')
    console.log('GET STORE')
    console.log(getStore)
    console.log("PROTOTYPE OF getStore")
    console.log(Object.getPrototypeOf(getStore))
    console.log('GET aTest')
    console.log(getStore.get('aTest'))

Upvotes: 1

Related Questions