Reputation: 4940
I am trying to apply module pattern in JavaScript when I have my custom constructor. Let us take this example:
// Assume I need `name` as private
function Dog(name){
this.name = name;
}
I can rewrite above as:
function Dog(name) {
let dogName = name;
return {
getName : function () {
return dogName;
},
setName : function (name) {
dogName = name;
}
}
}
The above code does not have right constructor
property and Dog.prototype
and the prototype of object returned
does not match.
To fix this I made 2 Fixes:
function Dog(name) {
let dogName = name;
return {
// Fix 1
constructor : Dog,
getName : function () {
return dogName;
},
setName : function (name) {
dogName = name;
}
}
}
let d2 = new Dog("Tony");
// Fix 2 : This is painful as every time I need to set this up
Object.setPrototypeOf(d2 , Dog.prototype);
As you can see Fix 2 is painful. Every time I have to create an object, I need to do this. Is there any better way?
Let us not deviate our discussion that getter and setter should be moved to prototype. Above is just a quick example I am using.
Upvotes: 2
Views: 96
Reputation: 2669
Disclaimer: I don't advocate encapsulation in javascript. But here are some ugly ways to do it.
If all you're looking for is reduce code duplication, then you could easily just set everything up in the "constructor" by making it its own factory:
function Dog(name) {
let dogName = name;
let dog = Object.create(Dog.prototype);
dog.getName = function () {
return dogName;
};
dog.setName = function (name) {
dogName = name;
};
return dog;
}
let d2 = new Dog("Tony");
If you also want to make actual use of the prototype you could use symbols to pretty much hide the name property:
Dog = function() {
const nameSymbol = Symbol("name");
function Dog(name) {
Object.defineProperty(this, nameSymbol, {
configurable: true,
enumerable: false,
writable: true,
value: name
});
}
Object.assign(Dog.prototype, {
getName : function () {
return this[nameSymbol];
},
setName : function (name) {
this[nameSymbol] = name;
}
});
return Dog;
}();
let d2 = new Dog("Tony");
But the symbol can still be retrieved via Object.getOwnPropertySymbols
. So if you're looking for a way to really make it impossible to access the name without using the getter/setter, I think you will have to use a WeakMap
:
Dog = function() {
const dogNames = new WeakMap()
function Dog(name) {
dogNames.set(this, name);
}
Object.assign(Dog.prototype, {
getName : function () {
return dogNames.get(this);
},
setName : function (name) {
dogNames.set(this, name);
}
});
return Dog;
}();
let d2 = new Dog("Tony");
Upvotes: 2
Reputation: 6063
The problem here is you're returning an object literal in your "constructor", an object literal has the Object
prototype. You should not be returning in the constructor, you should be assigning things to this
like you did in your first code snippet.
function Dog(name) {
let dogName = name;
this.getName = function () {
return dogName;
};
this.setName = function (name) {
dogName = name;
};
}
This isn't a common pattern for implementing classes though, you're not even using the prototype
so it's unclear why the prototype
is important to you. Obviously this approach allows you to close over a variable to make it "private" which wouldn't be possible using the prototype
. However, this approach has more overhead because each instance of Dog
will have it's own function for getName
and setName
, the standard convention for "private" variables and methods is to prefix them with an underscore so you can use the prototype
as intended.
function Dog(name) {
this._name = name;
}
Dog.prototype.getName = function () {
return this._name;
};
Dog.prototype.setName = function (name) {
this._name = name;
};
Upvotes: 2