add a method to a prototype - javascript

I have the following code
const humanUtils = {
sayRace: function(){
console.log('Im a '+ this.race);
}
}
function human() {
const human = Object.create(humanUtils);
human.race = 'human';
return human;
}
const manUtils = Object.create(humanUtils);
function man() {
const createMan = new human();
createMan.gender = 'man';
return createMan
}
function woman() {
const createWoman = new human();
createWoman.gender = 'woman';
return createWoman;
}
const mankind = new human();
const adam = new man();
const eve = new woman();
I'd like to add a method to manUtils() which will be only available to man, not to human and woman, such as i can call man.foo().
How can i make the constructor man() to point to manUtils and still accessing humanUtils functions so that i can call adam.sayRace() and adam.foo() inherited from manUtils ?
i don't want to use the new es6 class neither the traditional prototype reassignment (mdn)...if it is possible

Is there a reason for not putting the race property in the humanUtils? If not, just put the race there and you can make the constructor man() to point to manUtils and still access humanUtils' functions by writing
function man() {
return Object.create(manUtils)
}
And then, if you want to add a method to manUtils, simply write manUtils.fancyNewMethod = yourNewMethod
A note on new
In your example it makes no sense to use the new operator to construct mankind, adam and eve. When you write new human, a new object is created (lets call it A for reference) with as prototype human.prototype (which is an empty object, since you don't want to reassign humanUtils to human.prototype) and then human is executed with this bound to A. But inside your human constructor you discard A and instead create your own object using Object.create(humanUtils).
Full example
const humanUtils = {
sayRace() {
console.log('Im a '+ this.race);
},
race: 'human'
}
function human() {
return Object.create(humanUtils);
}
const manUtils = Object.create(humanUtils, {
gender: { value: 'man' }
});
function man() {
return Object.create(manUtils)
}
function woman() {
const createWoman = new human();
createWoman.gender = 'woman';
return createWoman;
}
const mankind = human();
const adam = man();
const eve = woman();
console.log(adam.gender)
adam.sayRace()
console.log('humanUtils isPrototypeOf adam:', humanUtils.isPrototypeOf(adam))
console.log('manUtils isPrototypeOf eve:', manUtils.isPrototypeOf(eve))
manUtils.saySlogan = function() {console.log('men are cool because they have a slogan')}
adam.saySlogan()

Related

Equivalence of object constructors

Often, when I create an object in Javascscript, I will do something like this:
function getObj(property) {
return {
property,
displayProp: function() {
console.log(this.property);
}
}
}
let obj = getObj("Hello World!);
I know that the usual way to create a constructor would be as such:
function getObj(property) {
this.property = property;
this.displayProp = function() {
console.log(this.property);
}
}
let obj = new getObj("Hello World!);
Now, my question is, are these two methods equivalent. As far as I can tell, they are the same, and you can also use the 'new' keyword when creating an object using the first case. For me, the first case makes more sense, but it doesn't matter too much to me. Thanks.
The first example is a function that returns a litteral object (which constructor will remain simply "Object").
The second one creates a new constructor.
function getObj(property) {
return {
property,
displayProp: function() {
console.log(this.property);
}
}
}
// this will be useless because the prototype of getObj is not related to the returned object
getObj.prototype.test_prop = "Test prop";
let obj = getObj("Hello World!");
console.log(obj.constructor.name) // Object
console.log(obj.test_prop) // undefined
function Obj(property) {
this.property = property;
this.displayProp = function() {
console.log(this.property);
}
}
Obj.prototype.test_prop = "Test prop";
obj = new Obj("Hello World!");
console.log(obj.constructor.name) // Obj
console.log(obj.test_prop) // Test prop

Javascript factory functions without this, new or prototype

I use more and more often this weird (at least for me) approach and I'm starting to like it but, at the same time, I'm not sure if there isn't something horribly wrong in a context of the bigger picture.
Simplified example:
function createCat(initName) {
let name = initName;
const getName = () => name;
function setName(newName) {
name = newName;
}
function meow() {
console.log("MEOW");
}
return {
meow,
setName,
getName
};
}
function createSuperCat(name) {
const proto = createCat(name);
const getName = () =>
`Super secret! But hey - they used to call himself ${name}`;
const getSecretName = () => proto.getName();
function bark() {
console.log("WHOOF");
}
return { ...proto, bark, getName, getSecretName };
}
const someCat = createCat("Hugo");
const someSuperCat = createSuperCat("Fluffy");
// someCat
console.log(someCat.getName()); // Hugo
someCat.setName("Tom");
console.log(someCat.getName()); // Tom
someCat.meow(); // MEOW
// someSuperCat
console.log(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy
console.log(someSuperCat.getSecretName()); // Fluffy
someSuperCat.setName("Mittens");
console.log(someSuperCat.getSecretName()); // Mittens
console.log(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy
someSuperCat.meow(); // MEOW
someSuperCat.bark(); // WHOOF
I'm aware that there's not such thing as the only correct way and if something works it's a valid solution.
But still - I'm just wondering...do you see any caveats in this approach?
So far it seems it can do all the fancy things like inherit, extend or override inherited method and you can omit the old-time-y OOP's this, you don't have to instantiate a standard factory function with new keyword, there's no prototype pattern (well, actually there kinda is - and since it's Javascript, there always will be), there aren't any classes or other syntactic sugar.
I may be wrong. I'm not a real programmer or something so I would appreciate an input from someone experienced because I'm probably missing something what can cause some discomfort later on.
The pattern is fine, as long as you realise the loss of features like:
instanceof operator,
Object.getPrototypeOf,
prototype and constructor properties
No copy of functions as you would have them on the prototype
There seems to also be an impact on performance.
Here is a comparison with an OOP implementation in modern syntax. Note that it uses private fields, which at the time of writing is not supported in all engines, notably not in FireFox. But for instance in Chrome it runs fine:
// Parasitic implementation
function createCat(initName) {
let name = initName;
const getName = () => name;
function setName(newName) {
name = newName;
}
function meow() {
return "MEOW";
}
return {
meow,
setName,
getName
};
}
function createSuperCat(name) {
const proto = createCat(name);
const getName = () =>
`Super secret! But hey - they used to call himself ${name}`;
const getSecretName = () => proto.getName();
function bark() {
return "WHOOF";
}
return { ...proto, bark, getName, getSecretName };
}
// OOP implementation
class Cat {
#name
constructor(initName) {
this.#name = initName;
}
get name() {
return this.#name;
}
set name(newName) {
this.#name = newName;
}
meow() {
return "MEOW";
}
}
class SuperCat extends Cat {
#name
constructor(initName) {
super(initName);
this.#name = initName;
}
get name() {
return `Super secret! But hey - they used to call himself ${super.name}`;
}
set name(newName) {
this.#name = newName;
}
get secretName() {
return this.#name;
}
bark() {
return "WHOOF";
}
}
function testParasitic() {
// First produce the output as by OP
let output = [];
const someCat = createCat("Hugo");
const someSuperCat = createSuperCat("Fluffy");
// someCat
output.push(someCat.getName()); // Hugo
someCat.setName("Tom");
output.push(someCat.getName()); // Tom
output.push(someCat.meow()); // MEOW
// someSuperCat
output.push(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy
output.push(someSuperCat.getSecretName()); // Fluffy
someSuperCat.setName("Mittens");
output.push(someSuperCat.getSecretName()); // Mittens
output.push(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy
output.push(someSuperCat.meow()); // MEOW
output.push(someSuperCat.bark()); // WHOOF
console.log(output.join(", "));
// Then run a performance test:
let start = performance.now();
for (let i = 0; i < 1000; i++) {
const someCat = createCat("Hugo");
someCat.setName("Tom");
someCat.getName(); // Tom
someCat.meow(); // MEOW
for (let j = 0; j < 100; j++) {
const someSuperCat = createSuperCat("Fluffy");
someSuperCat.setName("Mittens");
someSuperCat.getSecretName();
someSuperCat.getName();
someSuperCat.meow();
someSuperCat.bark();
}
}
return performance.now() - start;
}
function testOOP() {
// First produce the output as by OP
let output = [];
const someCat = new Cat("Hugo");
const someSuperCat = new SuperCat("Fluffy");
// someCat
output.push(someCat.name); // Hugo
someCat.name = "Tom";
output.push(someCat.name); // Tom
output.push(someCat.meow()); // MEOW
// someSuperCat
output.push(someSuperCat.name); // Super secret! But hey - they used to call him Fluffy
output.push(someSuperCat.secretName); // Fluffy
someSuperCat.name = "Mittens";
output.push(someSuperCat.secretName); // Mittens
output.push(someSuperCat.name); // Super secret! But hey - they used to call him Fluffy
output.push(someSuperCat.meow()); // MEOW
output.push(someSuperCat.bark()); // WHOOF
console.log(output.join(", "));
// Then run a performance test:
let start = performance.now();
for (let i = 0; i < 1000; i++) {
const someCat = new Cat("Hugo");
someCat.name = "Tom";
someCat.name;
someCat.meow();
for (let j = 0; j < 100; j++) {
const someSuperCat = new SuperCat("Fluffy");
someSuperCat.name = "Mittens";
someSuperCat.secretName;
someSuperCat.name;
someSuperCat.meow();
someSuperCat.bark();
}
}
return performance.now() - start;
}
let dur1 = testParasitic();
let dur2 = testOOP();
console.log("duration parasitic test", Math.round(dur1));
console.log("duration OOP test", Math.round(dur2));
So one of the biggest caveats to this approach is firstly, adding unecessary complexity to your code, since JS already has solutions to what you're doing so you're not really achieving anything by doing it this way. But also this approach will be a a lot more inefficient and waste memory as you increase the amount of objects. One benefit of prototypical inheritance and the this keyword is that all you functions can be stored within a single object reference
Your approach redefines all functions for each object. So every time you create a new cat object you are redefining all your generic functions. Which are being stored in memory for each object.
Even if you want to avoid classes and using new, it would be better to take advantage of prototypical inheritance and the this keyword.
I rewrote your code as such:
const catProto = {
getName: function () {
return this.name;
},
setName: function (newName) {
this.name = newName;
},
meow: function () {
console.log("MEOW");
},
};
function createCat(name) {
const cat = Object.create(catProto);
cat.name = name;
return cat;
}
function createSuperCat(name) {
const cat = createCat(name);
cat.getName = () => `Super secret...`;
cat.getSecretName = catProto.getName;
cat.bark = () => console.log("WHOOF");
return cat;
}
const someCat = createCat("Hugo");
const someSuperCat = createSuperCat("Fluffy");
// someCat
console.log(someCat.getName()); // Hugo
someCat.setName("Tom");
console.log(someCat.getName()); // Tom
someCat.meow(); // MEOW
// someSuperCat
console.log(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy
console.log(someSuperCat.getSecretName()); // Fluffy
someSuperCat.setName("Mittens");
console.log(someSuperCat.getSecretName()); // Mittens
console.log(someSuperCat.getName()); // Super secret! But hey - they used to call him Fluffy
someSuperCat.meow(); // MEOW
someSuperCat.bark(); // WHOOF

How to set proxy on constructor function

I have a constructor function like this:
function a(p){
this.k=12;
this.f = p;
this.t = function(){};
}
Now let say i create a object with this function:
let obj = new a("tf");
I can always do this to check if a new property assigned to object:
obj = new Proxy(obj,{set:()=>{console.log("new property assigned!");return true;}})
//Now if i were to say
obj.property1 = 12
//It will log out "new property assigned!"
But the problem is, that i have to do this for every new object i create with this constructor function.
So what i want to do is whenever i create a new object like this:
let newObj = new a();
I want to set my proxy automatically.
How can i achieve this?
Write a factory function in which you create a new instance of your object while also adds a Proxy to the newly created instance.
function proxyFactory(Class, ...args) {
const instance = new Class(...args);
return new Proxy(instance, {
set: () => {
console.log("new property assigned!");
return true;
}
});
}
And then call the factory function with the class and the arguments you want to apply.
let newObj = proxyFactory(a, "tf");
You could also make a proxy around class, intercept construct calls, and return a proxy with set trap.
const A = new Proxy(a, {
construct(target, args) {
return new Proxy(new target(...args), {
set() {
console.log('new property assigned');
return true
}
})
}
})
const inst = new A('tf')
inst.property = true
function a(p) {
this.k = 12;
this.f = p;
this.t = function() {};
}
You could make a function that wraps around your class. E.g.
const proxyify = (Class, ...args) => {
return new Proxy(new Class(...args), {
set: () => {
console.log('set');
return true;
}
});
}
Usage would then be
function Dog() {
this.type = "Dog";
}
const ProxiedDog = proxify(Dog);

implement static,private members using prototypal pattern in javascript

this is prototypal model in javascript.
unlike use constructor function with new keyword in this pattern we are using
existing object to create a new object as shown below
var answerprototype = {
name:'ss',
get:function fn1()
{
return "--";
}
}
var lifeanswer = Object.create(answerprototype);
this is how implement private ,public members in javascript using classical pattern
function Restaurant()
{
//private varible
var myPrivateVar = 'aaa';
//public variable
this.myPublicVar = 'bbbb'
//private method
// Only visible inside Restaurant()
var private_stuff = function()
{
return "private metho";
}
//public method
// use_restroom is visible to all
this.use_restroom = function()
{
return "public method"
}
}
//create object
var r1 = new Restaurant();
r1.myPrivateVar //return undefined
r1.myPublicVar //return "bbbb"
r1.private_stuff() //error
r1.use_restroom() //return "public method"
this is how implement static members in javascript using classical pattern
function Shape(shapeName)
{
//instance field
this.ShapeName = shapeName;
//static field
Shape.Count = ++Shape.Count;
//static method
Shape.ShowCount = function()
{
return Shape.Count;
}
}
var shape1 = new Shape("circle");
var shape1 = new Shape("rectangle");
var shape1 = new Shape("Triangle");
Shape.ShowCount(); //return 3
i want to implement static members and private ,public members in javascript using prototypal pattern(not using new keyword with constructor function)
how to do it
In JavaScript, private doesn't mean what you think it means. You have to create a closure which is where you trap your "private" values.
Using a function is the traditional way to create a closure
function makeProto(chainTo) {
const secret = 'foo';
return Object.assign(Object.create(chainTo || null), {
get hello() {return secret;}
});
}
let fizz = makeProto();
fizz.hello; // "foo"
Object.prototype.hasOwnProperty.call(fizz, 'hello'); // true
With ES6, you can use the block-scoped let or const in combination with the function scoped var to achieve something similar
;{
let foo = 'bar';
var obj = {
get baz() {return foo;}
};
}
obj.baz; // "bar"
foo; // ReferenceError
Please remember that as soon as you inherit from one of these objects, all descendant objects will share the same references to these values, not create new ones
I wrote ;{ /* ... */ } so if you try the code in your console it doesn't attempt to interpret this pattern as an Object literal, but instead a code block. In production the initial ; is not necessary.

JavaScript prototypal inheritance to show parent object name

I've been reading about JavaScript prototypal inheritance and the prototype property, and I started making a fiddle to understand the concepts better. But I need help understanding why my example isn't working as I thought it would.
In the following example, I'm trying to create a hierarchy where when you create an object, it tells you the name of the parent object. However, my logs always return "object".
One article explained that prototype chaining works so that if a property is not found on the object, the prototype of the parent is checked, and it keeps going up until 'Object', and if not found returns undefined.
Here's a fiddle to go along: http://jsfiddle.net/hqozqd0m/
Object.prototype.cname = 'object';
function plant() {
function parentname() { return this.cname; }
return {
parentname:parentname
}
}
plant.prototype.cname = 'plant';
function tomato() {
function parentname() { return this.cname; }
return {
parentname:parentname
}
}
tomato.prototype = new plant(); // <-- settings it to plant as parent
var p = new plant();
var t = new tomato();
console.log(p.parentname()); //object
console.log(t.parentname()); //object
updated code - same result
Object.prototype.cname = 'object';
function plant() {
this.sayparentname = function() { console.log(cname); };
}
plant.prototype.cname = 'plant';
function tomato() {
this.sayparentname = function() { console.log(cname); };
}
tomato.prototype = new plant();
var p = new plant();
var t = new tomato();
p.sayparentname();
t.sayparentname();
Normally a constructor function will modify the object that new creates, with statements such as this.foo = bar, and not return anything. Then the result of the new expression is the object.
However, if the function returns an object, that object will replace the one that new created; so when you use new plant() you're just getting a plain Object instance back.
To fix your code you just need to make it like this:
function Plant() {
function parentName() { return this.cname; }
this.parentName = parentName;
}

Categories