How can I attach prototype to Object Literals in JS
Asked Answered
M

3

4

I am trying to understand JavaScripts Prototypal nature and I am trying to create object inheritance without using class like constructor functions.

How can I attach the prototype chain from Animals and then each to Cat and Dog if they are all three object literals?

Also, is there a way to do an Object.create and add more properties in literal form (like myCat).

I've added the code below & to paste bin http://jsbin.com/eqigox/edit#javascript

var Animal = {
    name        : null,
    hairColor   : null,
    legs        : 4,

    getName : function() {
        return this.name;
    },
    getColor : function() {
        console.log("The hair color is " + this.hairColor);
    }
};

/* Somehow Dog extends Animal */

var Dog = {
    bark : function() {
        console.log("Woof! My name is ");
    }
};

/* Somehow Cat Extends Animal */

var Cat = {
    getName : function() {
        return this.name;
    },

    meow : function() {
        console.log("Meow! My name is " + this.name);
    }
};


/* myDog extends Dog */

var myDog = Object.create(Dog);

/* Adding in with dot notation */
myDog.name = "Remi";
myDog.hairColor = "Brown";
myDog.fetch = function() {
    console.log("He runs and brings back it back");
};


/* This would be nice to add properties in litteral form */
var myCat = Object.create(Cat, {
    name        : "Fluffy",
    hairColor   : "white",
    legs : 3, //bad accident!
    chaseBall : function(){
        console.log("It chases a ball");
    }

});

myDog.getColor();
Merrymaking answered 9/7, 2012 at 1:33 Comment(1)
You can't change the prototype of an existing object except via the non-standard __proto__ property.Harlene
G
3

You can use Object.create to use your defined objects as prototypes. For instance:

var Dog = Object.create(Animal, {
    bark : {
        value: function() {
            console.log("Woof! My name is " + this.name);
        }
    }
});

Now you can create a new Dog object:

var myDog = Object.create(Dog);
myDog.bark();  // 'Woof! My name is null'
myDog.getColor();  // 'The hair color is null'

Example: http://jsfiddle.net/5Q3W7/1/

Alternatively, if you're working in the absence of Object.create, you can use constructors:

function Animal() {
    this.name = null;
    this.hairColor = null;
    this.legs = 4;
};

Animal.prototype = {
    getName : function() {
        return this.name;
    },
    getColor : function() {
        console.log("The hair color is " + this.hairColor);
    }
}

function Dog() {
}

Dog.prototype = new Animal;
Dog.prototype.bark = function() {
    console.log("Woof! My name is " + this.name);
};

Example: http://jsfiddle.net/5Q3W7/2/

For more information about Object.create: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create/

For more information about Constructors and prototypes chains: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor

https://developer.mozilla.org/en/JavaScript/Guide/Inheritance_and_the_prototype_chain

Gemology answered 9/7, 2012 at 1:52 Comment(1)
Wow Thanks! this is exactly what I was looking for :) I didn't realize that was built into ES5... very cool!!Merrymaking
H
0

edit: Totally overlooked Object.create. @jMerliN's answer is more appropriate, unless you need more extensive backwards compatibility.


Animal, Cat and Dog do share Object.prototype since they are instances of Object but that's not where you want to put your methods.

You can mimick inheritance by using an extend function:

var Dog = {
    bark : function() {
        console.log("Woof! My name is ")
    }
}

jQuery.extend(Dog, Animal)

That could be underscore's _.extend or your own extend function, which simply copies over it's properties:

function extend(obj, source){
    for (var prop in source) {
        obj[prop] = source[prop]
    }
} 

Be careful with references though: "primitive" values like strings, numbers, booleans will be copied over, while objects, functions and arrays will be referenced:

var Animal = {
    tags: [1,2,3]
}

var Dog = {
    //...
}

_.extend(Dog, Animal)

Dog.tags.push(4)

Dog.tags // [1,2,3,4]
Animal.tags // [1,2,3,4] oh no!

There's no reason to go through this, it's error prone and less memory-efficient; just use constructors :)

Halfhour answered 9/7, 2012 at 1:36 Comment(9)
I don't see a jQuery tag on the OP, nor does it seem appropriate to use such a large library for this, particularly when the OP is attempting to understand javascript (i.e. ECMAScript). Also, the extend function check for own properties.Brevier
Object.create sets [[Prototype]] too: it's not just constructors that do.Afeard
@Brevier come on, I said an extend function, mentioned underscore and gave the implementation code (underscore's, which doesn't do the own check). No need for the jQuery hate.Halfhour
@RicardoTomasi—copying object properties from one objet to another isn't inheritance, and expecting the OP to follow the jQuery extend code is asking way too much (particularly when it contains methods like isPlainObject).Brevier
This is interesting though... Just out of curiosity, is that what coffeescript is doing here: coffeescript.org/…Merrymaking
@SkinnyGeek1010 not really. CoffeeScript is using Crockford's inheritance trick (javascript.crockford.com/prototypal.html) plus a few additions, that's real prototypal inheritance.Halfhour
@Brevier I feel like you did a grep jQuery and didn't actually read my answer. It says mimick inheritance at the top, and provided a very simple extend method (not jQuery's)...Halfhour
@RicardoTomasi Thanks! After reading 'The Good Parts', not using 'classes' and embracing the prototype seemed logical. I wish JS had a built in version of the CoffeeScript way of extending prototypes! Do you see any potential issues with using the coffee compiled version and manually using __extends(child, parent) in raw JavaScript?Merrymaking
@SkinnyGeek1010 of course not! it has no dependencies. Note that if you're in a modern environment, you can achieve the same with child = function(){}; child.prototype = Object.create(parent.prototype); child.prototype.X = function(){...}Halfhour
I
-1

With ES2015 and later, you can use the literal __proto__ as the property name to setup the prototype right in the object literal:

const myProto = {
  propertyExists: function(name) {
    return name in this;
  },
};
const myNumbers = {
  __proto__: myProto,
  array: [1, 6, 7],
};
myNumbers.propertyExists('array'); // => true
myNumbers.propertyExists('collection'); // => false

Here is a nice post about this and where I got the example from.

Inchworm answered 1/7, 2023 at 17:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.