How to add a getter to an object - javascript

This probably has a prior answer, but I haven't been able to find it.
I have some code that gets data from a database and returns an entity such as:
var customer1 = {first: "Greg", last: "Gum"};
How do I add a getter to this object so that I can call a FullName getter:
var theFullName = customer1.fullName;
What I don't understand is the difference between adding a getter to an object, and adding a getter to a "class" so that it becomes available to all future objects of that type. The thing is that the code that creates the object is a black box, so I don't really have access to that code. I don't know the best way to resolve this.

If you just want to define the property on the object literal that has been returned, simply use Object.defineProperty:
var cdata = {first: 'Foo', last: 'Bar'}
Object.defineProperty(cdata, 'fullName', {
get: function() { return this.first + ' ' + this.last; }
});
console.log(cdata.fullName); // Foo Bar
However, if you want to create a new object from the returned literal, one method would be:
function Customer(data) {
var k;
for (k of Object.keys(data)) {
Object.defineProperty(this, k, {
value: data[k]
});
}
Object.defineProperty(this, 'fullName', {
get: function() { return this.first + ' ' + this.last; }
});
}
var cdata = {first: 'Foo', last: 'Bar'};
var customer = new Customer(cdata);
console.log(customer.fullName); // Foo Bar
But a more memory efficient method is:
function Customer(data) {
var k;
for (k of Object.keys(data)) {
Object.defineProperty(this, k, {
value: data[k]
});
}
}
Customer.prototype = {
get fullName() {
return this.first + ' ' + this.last;
}
};
var cdata = {first: 'Foo', last: 'Bar'};
var customer = new Customer(cdata);
console.log(customer.fullName); // Foo Bar
The first Customer definition adds an instance property fullName which will consume memory for each instance of Customer that is created. The second definition defines the fullName property as a prototype property, thus saving a small amount of memory.

EDIT: You are looking for this:
var customer1 = {first: "Greg", last: "Gum"};
Object.defineProperty(customer1, "fullName", {
get: function() { return this.first + ' ' + this.last; }
});
var theFullName = customer1.fullName;
A more general aproach:
function addGetter(obj, name, func) {
Object.defineProperty(obj, name, {
get: func
});
}
var customer1 = {first: "Greg", last: "Gum"};
addGetter(customer1, "fullName", function() { return this.first + ' ' + this.last; } )
var theFullName = customer1.fullName;
You could even protoype Object to do sth like customer1.getter("fullName", function() { return this.first + ' ' + this.last; } )
OLD:
I have a nice way of doing it with TypeScript, it compiles to this JavaScript:
var Customer = (function () {
function Customer(first, last) {
this.first = first;
this.last = last;
}
Object.defineProperty(Customer.prototype, "fullName", {
get: function () { return this.first + " " + this.last; },
enumerable: true,
configurable: true
});
return Customer;
})();
var customer1 = new Customer("Greg", "Gum");
var theFullName = customer1.fullName;
However the TypeScript looks way nicer:
class Customer {
first: string
last: string
constructor(first: string, last: string) {
this.first = first
this.last = last
}
get fullName() { return this.first + " " + this.last }
}
var customer1 = new Customer("Greg", "Gum")
var theFullName = customer1.fullName
You can play with it here

You have to create a constructor to use prototypal inheritance in JavaScript.
E.g. like below. Check JS Fiddle.
var customer1Data = {first: "Greg", last: "Gum"};
var Customer = function(customerData ) {
this.first = customer1Data.first;
this.last = customer1Data.last;
}
Customer.prototype.fullName = function() {
return this.first + ' '+this.last;
}
var customer1 = new Customer(customer1Data );

One way you can solve this is to create your own wrapper class Customer that wraps the received object and adds the getter.
Furthermore, you want to add the getter on the prototype of your Costumer class so that it is only defined once and available for all future instances. Something like:
function Customer(c) {
this.firstName = c.first;
this.lastName = c.last;
}
Customer.prototype.getFullName = function() {
return this.firstName + " " + this.lastName;
}
And you will use it like:
var c = getCustomerFromBlackBox();
var customer = new Customer(c);
If you are using ES6, then you can take advantage of its more robust syntax:
class Customer {
constructor(c) {
this.firstName = c.first;
this.lastName = c.last;
}
fullName() {
return this.firstName + " " + this.lastName;
}
}
Based on your comment that you want to edit the received object itself, well then you can do just that:
var c = getCustomerFromBlackBox();
if(typeof c === 'object' && c !== null) { // make sure c is an object
c.getFullName = function() { // add a getter
return this.first + " " + this.last;
}
}
Do note that this will add the getter code to every object you receive and thus will take more memory.
Also, since you don't know how this object is created, you might want to add checks to make sure first and last strings to actually exist and perhaps add specific values in case they don't.
EDIT:
Since you said in your comment that you know that your first and last names will never change, and that you want a property rather than a getter function, you can literally just add this property each time you read a customer:
var c = getCustomerFromBlackBox();
c.fullName = c.first + " " + c.last; // Can also add same checks to make sure c is an object and c.first and c.last are strings
Again, this is added to every object and has a similar memory overhead as adding a direct getter function (and not on the prototype)
A good approach that I missed is using Object.defineProperty as shown in James Sumner's answer.
Object.defineProperty(c, 'fullName', {
get: function() { return this.first + " " + this.last; }
});
This approach adds the property directly on the object (not the prototype) and additionally makes the property immutable meaning you cannot accidentally delete it.

Related

Object.prototype does not make right inheriting

I have a code that based on class inheritance. The Rabbit constructor inherit methods from Animal prototype and then create an object rabbit with Animal methods.
I can not understand - why the Rabbit constructor does not want inherit the Animal.prototype.name method. It's exactly as an Animal.prototype.run, but it does not work...
function Animal(name) {
this.name = name;
this.speed = 0;
}
Animal.prototype.run = function(speed) {
this.speed += speed;
console.log( this.name + ' run, speed ' + this.speed );
};
Animal.prototype.name = function(name) {
this.name = name;
console.log( 'Rabbit name ' + this.name );
};
Animal.prototype.stop = function() {
this.speed = 0;
console.log( this.name + ' stay' );
};
function Rabbit(name) {
Animal.apply(this, arguments);
}
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;
Rabbit.prototype.jump = function() {
this.speed++;
console.log( this.name + ' jump' );
};
var rabbit = new Rabbit('Rabbit');
rabbit.name(); // Error, but it must display "Rabbit"
rabbit.run(); // works fine
rabbit.jump(); // works fine
.name is not a method, in the constructor it gets assigned the argument string. This own property shadows the inherited method. Calling the string will produce the error.
Change it to
Animal.prototype.setName = function(name) {
this.name = name;
console.log( 'Rabbit name ' + this.name );
};
…
console.log(rabbit.name); // displays "Rabbit"
rabbit.setName("Roger"); // displays "Rabbit name Roger"
You have object field "name" and function "name". Field is overriding the function.
Solution rather simple - rename one or another.

JavaScript Object to replicate an object

Working on a Javascript lesson and I'm stuck trying to figure out how to answer/understand how to complete this question wanted to know if anyone could help.
function Container(param) {
var person = {
firstName: 'Jimmy',
lastName: 'Smith',
get fullName() {
return this.firstName + ' ' + this.lastName;
},
set fullName (name) {
var words = name.toString().split(' ');
this.firstName = words[0] || '';
this.lastName = words[1] || '';
}
}
}
// Attempting to clone private getter don't know how to access it.
function objectClone(person) {
var orginal = person //Trying to access the private method
var clone = function cloneObj { Object.assign({}, original); }
clone.prototype.spillSecret = function() { alert(this.getfullName()); }
;}
Use Object.assign to copy the normal properties, and then copy the special properties by hand after:
copy = Object.assign({}, original);
copy.year = original.year;
But this doesn't use a closure anywhere, so I'm not sure if it's what your tutorial is looking for.
var Person = function(person){
var name = 'John Doe';
function copy(){
var newPerson = Object.assign({}, person);
Object.defineProperty(newPerson, 'name', {
get: function() { return name; },
set: function(newName){ if(typeof newName !== 'string') {console.error('invalid name')} else name = newName},
enumerable: true,
configurable: true
});
return newPerson;
}
return copy();
};
var person1 = {
age: 20,
location: 'India'
}
var person2 = Person(person1);
var person3 = Person(person1);
person3.name = 1234; // invalid name
person3.name = 'Mr. India';
console.log(person1.name); // undefined
console.log(person2.name); // John Doe
console.log(person3.name); // Mr. India

setting property as function in a constructor javascript

i'm working with a piece of code that sets a name property as a function
name = n;
this.name = function(n){
if( n ) name=n;
return name;
}
full constructor is:
function Warrior(n){
name = n;
this.name = function(n){
if( n ) name=n;
return name;
}
}
I don't understand how this is different than this.name = n; and why I can't use that in a toString function like
Person.prototype.toString = function(){
return "Hi! my name's "+this.name;
}
What you created is a getter/setter for name:
A getter is a function which returns a value associated with an object property.
A setter is a function which updates the value associated with an object property.
Getters and setters are useful for creating “derived properties” (i.e. properties which are derived from other properties). For example:
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
Person.prototype.fullname = function (fullname) {
if (arguments.length < 1) return this.firstname + " " + this.lastname;
var newname = fullname.split(" ");
this.firstname = newname[0];
this.lastname = newname[1];
return fullname;
};
var person = new Person("Aadit", "Shah");
alert(person.firstname);
alert(person.lastname);
alert(person.fullname());
person.fullname("Chuck Norris");
alert(person.firstname);
alert(person.lastname);
alert(person.fullname());
In the above code fullname is a derived property. It is derived from firstname and lastname. As you can see, when I update fullname, I am also updating firstname and lastname. Hence getters and setters are useful in this case.
However a lot of people misuse getters and setters. For example:
function Warrior(name) {
this.name = function (newName) {
if (arguments.length >= 1)
name = newName;
return name;
}
}
In this case there's no good reason to create a getter/setter for name because it is not a derived property. It's a misuse of getters/setters and all it does is make the code slower. Instead it should be implemented as:
function Warrior(name) {
this.name = name;
}
Plain and simple.
Now consider:
Person.prototype.toString = function () {
return "Hi! my name's " + this.name;
};
The reason this doesn't work is because this.name is a getter function. Hence you need to call it to get the actual value. Thus, it should be:
Person.prototype.toString = function () {
return "Hi! my name's " + this.name();
};
Finally, newer versions of JavaScript have built in support for getters and setters:
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
Object.defineProperty(Person.prototype, "fullname", {
get: function () {
return this.firstname + " " + this.lastname;
},
set: function (fullname) {
var newname = fullname.split(" ");
this.firstname = newname[0];
this.lastname = newname[1];
}
});
var person = new Person("Aadit", "Shah");
alert(person.firstname);
alert(person.lastname);
alert(person.fullname);
person.fullname = "Chuck Norris";
alert(person.firstname);
alert(person.lastname);
alert(person.fullname);
This makes getters and setters look like normal properties. Such properties are called accessor properties.
Here your constructor function is Warrior and you can add new method toString to an existing prototype Warrior. Like this you can add.
Warrior.prototype.toString = function(){ // code goes here }
this.name is a function .so
Warrior.prototype.toString = function(){
return "Hi! my name's "+this.name();
}
Using new keyword to create new objects from the same prototype
Warrior.prototype.toString = function(){
return "Hi! my name's "+this.name();
}
myname = new Warrior("ur name");
console(myname.toString());

Javascript, why 'for(v in obj)' and 'obj.hasOwnProperty' can have inconsistent behaviour when the obj is created by different ways?

I have three ways to create objects:
function createC() {
function Person(name){
this.name = name;
this.getName = function(){
return this.name;
};
}
return new Person("Diego");
}
function createD() {
function Person() { };
Person.prototype.name = "Diego";
Person.prototype.getName = function () {
return this.name;
};
return new Person();
}
function createE() {
return Object.create(Object.prototype, {
name: { value: "Diego" },
getName: { value: function () { return this.name; } }
});
}
Three objects from each can be used as same, but they have different results when checking their own properties. Here is the function I wrote to check the properties:
function checkOwnProperies(obj, name) {
console.log('check object ' + name + '\'s own properties:');
var bIn = false;
for (var v in obj) {
bIn = true;
if (obj.hasOwnProperty(v)) {
console.log('Yes! Found own property: ' + v);
}
else {
console.log('Found a property, but it\'s not own: ' + v);
}
}
if (!bIn)
console.log("Didn\'t find any members in " + name + '!');
}
The result I got:
oC name: Diego
oD name: Diego
oE name: Diego
check object oC's own properties:
Yes! Found own property: name
Yes! Found own property: getName
check object oD's own properties:
Found a property, but it's not own: name
Found a property, but it's not own: getName
check object oE's own properties:
Didn't find any members in oE!
The complete snippet: http://codepen.io/anon/pen/homGx
Can someone please explain why the difference?

functions nested under a prototype function behave differently

Functions nested under a prototype function do not get the this.variables defined for the prototype.
var Person, p;
Person = function(name, age) {
this.name = name;
this.age = age;
};
Person.prototype.getInfo = function() {
var innerFun;
innerFun = function() {
return this.name;
};
return "name: " + (innerFun()) + " age: " + this.age;
};
p = new Person('dork', 99);
console.log(p.getInfo()); // name: age: 99
I thought that since every function is an object, this would be different within every function definition; but the following code blows away that logic.
var getInfo;
getInfo = function() {
var display;
this.name = 'dork';
return display = function() {
return this.name;
};
};
console.log(getInfo()()); // dork
Is there a logic behind this behaviour or should I just take it as a rule and use call() to get around this problem?
Short version of this behavior:
In function invoked as x.f(), this will be x.
(Note: invoked as x.f(). It does not matter how and where you define it)
In function invoked as f(), this will be window.
In function invoked as x['f'](), this will be window x (not sure why I thought otherwise)
In function invoked as f.call(x) or f.apply(x), this will be x.
Again, the prototype does not matter, if you do
var f = p.getInfo;
console.log(f());
you will see that only calling style matters.
Summarizing Conclusions:
Rule: If no context is used for a function call, the default context is the global window object
this.name = 'global';
console.log(p.getInfo()); // name: global age: 99
solution1
use call/apply
return "name: " + (innerFun.call(this)) + ": Age " + this.age;
solution2
Use this for inner functions
this.innerFun = function() {
return this.name;
};
return "name: " + (this.innerFun()) + ": Age " + this.age;
solution3
self = this
innerFun = function() {
return self.name;
};

Categories