JavaScript Object to replicate an object - javascript

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

Related

Aliasing or otherwise merging two identical object prototypes with different names

I've got two object prototypes like this:
function Tag(name, description) {
this.name = name;
this.description = description || null;
}
function Category(name, description) {
this.name = name;
this.description = description || null;
}
Both of them are exactly the same, which seems awkward. Is it possible to merge them both into an object named 'Entity', and refer to them both by different names (the original 'Tag' and 'Category')?
This may be further complicated by the fact I need to refer to the current prototype name inside the prototype.
Tag.prototype.toJSON = function() {
return {
__type: 'Tag',
name: this.name,
description: this.description
};
};
How can I apply the same 'toJSON' extension to the 'Entity' object, but make sure it returns 'Tag' or 'Category' in the '__type' field, dependent on which object is being used?
I would do something like this:
Dummy = function () {};
Entity = function (name) {
this.name = name;
};
Entity.prototype.toString = function () {
return "My name is " + this.name + ".";
};
A = function () {
Entity.call(this, 'A');
};
Dummy.prototype = Entity.prototype;
Dummy.prototype.constructor = A;
A.prototype = new Dummy();
B = function () {
Entity.call(this, 'B');
};
Dummy.prototype = Entity.prototype;
Dummy.prototype.constructor = B;
B.prototype = new Dummy();
document.body.innerHTML = ""
+ (new A()) + "<br />"
+ (new B());
Here is a small function to make things cleaner (hopefully):
function Nothing () {};
function extend (Sup, proto) {
function Class () {
if (this.init) {
this.init.apply(this, arguments);
}
}
Nothing.prototype = Sup.prototype;
Nothing.prototype.constructor = Sup;
Class.prototype = new Nothing();
delete Nothing.prototype;
for (var k in proto) {
Class.prototype[k] = proto[k];
}
return Class;
}
Here is how to use it:
Entity = extend(Nothing, {
init: function (name) {
this.name = name;
},
toString: function () {
return "My name is " + this.name + ".";
}
});
A = extend(Entity, {
init: function () {
var sup = Entity.prototype;
sup.init.call(this, 'A');
}
});
B = extend(Entity, {
init: function () {
var sup = Entity.prototype;
sup.init.call(this, 'B');
}
});

How to add a getter to an object

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.

Creating a Person - Setting and Changing first and last name

I am trying to build a function that creates a person by receiving at first a full name and using methods to set the first, last and full name. Afterwards it would have methods that should be able to change each part of the name.
My current code looks like this:
var Person = function(firstAndLast) {
var name = firstAndLast;
this.getFirstName = function() {
return name.substr(0,name.indexOf(' '));
};
this.getLastName = function() {
return name.substr(name.indexOf(' ')+1);
};
this.getFullName = function() {
return name;
};
this.setFirstName = function() {
return name;
};
this.setLastName = function() {
return name;
};
this.setFullName = function() {
return name;
};
};
var bob = new Person('Bob Ross');
bob.setFullName();
Now I am completely stuck when it gets time to pass a new name, so that if I do something like:
bob.setFullName('George Carlin');
and then pass:
bob.getFullName();
I should get the answer 'George Carlin'.
yet this isn't happening.
Thanks as always.
So the problem was that I wasn't really understanding how the values where being passed, and that instead of working on a full name I should be working on the parts.
var Person = function(firstAndLast) {
var firstName = firstAndLast.split(' ')[0];
var lastName = firstAndLast.split(' ')[1];
this.setFirstName = function(firstNameNew) {
firstName = firstNameNew;
};
this.setLastName = function(lastNameNew) {
lastName = lastNameNew;
};
this.setFullName = function (fullNameNew) {
firstName = fullNameNew.split(' ')[0];
lastName = fullNameNew.split(' ')[1];
};
this.getFullName = function() {
return firstName + ' ' + lastName;
};
this.getFirstName = function() {
return firstName;
};
this.getLastName = function() {
return lastName;
};
};
var bob = new Person('Bob Ross');

Write getters as a prototype

I'm working on making performance updates on my javascript code.
In Firefox I got this warning:
mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create
I wrote some scripts to prove this, and the results are great: without mutation a simple script runs 66% faster.
But I have trouble converting my code without mutation, I can't write the getters:
This is what I have now:
// Class
function FooBar(options) {
this.options = options;
}
// Prototype
FooBar.prototype = {
// Getters
get a() {
return this.options.a;
},
get b() {
return this.options.b;
},
get ab() {
return this.options.a + this.options.b;
},
// Methods
displayOptions: function() {
console.log(this.options);
}
};
// Code
var options = {
a: 'foo',
b: 'bar'
};
var fooBar = new FooBar(options);
console.log(fooBar.a);
console.log(fooBar.b);
console.log(fooBar.ab);
fooBar.displayOptions();
The getters as a prototype using the this keyword in their return are the problem.
If I use Object.defineProperty the this keyword is wrong, unless I do it inside the constructor, but it would recreate the property on each instance of the class and slow my code down even further.
This works (I just messed up the syntax in my previous attempt):
// Class
function FooBar (options) {
this.options = options;
}
//Prototype getters
Object.defineProperty(FooBar.prototype, 'a', {
get: function() {
return this.options.a;
}
});
Object.defineProperty(FooBar.prototype, 'b', {
get: function() {
return this.options.b;
}
});
Object.defineProperty(FooBar.prototype, 'ab', {
get: function() {
return this.options.a + this.options.b;
}
});
// Methods
FooBar.prototype.displayOptions = function() {
console.log(this.options);
};
// Code
var options = {
a:'foo',
b:'bar'
};
var fooBar = new FooBar (options);
console.log(fooBar.a);
console.log(fooBar.b);
console.log(fooBar.ab);
fooBar.displayOptions();
For those who are curious about the benefits of converting scripts like this to run faster: Run following code and look to your output in the console (Chrome - 66% faster, Firefox - no difference (curious, since I got the warning from Firefox)):
// WITHOUT PROTOTYPING
var Person1 = function() {
this.name = 'myName';
this.changeName = function(name) {
this.name = name;
};
this.changeName2 = function(name) {
this.name = name;
};
this.changeName3 = function(name) {
this.name = name;
};
this.changeName4 = function(name) {
this.name = name;
};
}
// WITH PROTOTYPING, WITH MUTATION
var Person2 = function() {
this.name = 'myName';
}
Person2.prototype = {
changeName: function(name) {
this.name = name;
},
changeName2: function(name) {
this.name = name;
},
changeName3: function(name) {
this.name = name;
},
changeName4: function(name) {
this.name = name;
}
};
// WITH PROTOTYPING, WITHOUT MUTATION
var Person3 = function() {
this.name = 'myName';
}
Person3.prototype.changeName = function(name) {
this.name = name;
};
Person3.prototype.changeName2 = function(name) {
this.name = name;
};
Person3.prototype.changeName3 = function(name) {
this.name = name;
};
Person3.prototype.changeName4 = function(name) {
this.name = name;
};
// DO THE TEST
var i=0, len=1000000;
// TEST1
window.performance.mark('mark_test_start');
for(i=0;i<len;i++) {
p = new Person1();
p.changeName('myName2');
}
window.performance.mark('mark_test_end');
window.performance.measure('no-prototyping', 'mark_test_start', 'mark_test_end');
// TEST2
window.performance.mark('mark_test2_start');
for(i=0;i<len;i++) {
p = new Person2();
p.changeName('myName2');
}
window.performance.mark('mark_test2_end');
window.performance.measure('prototyping-with-mutation', 'mark_test2_start', 'mark_test2_end');
// TEST3
window.performance.mark('mark_test3_start');
for(i=0;i<len;i++) {
p = new Person2();
p.changeName('myName2');
}
window.performance.mark('mark_test3_end');
window.performance.measure('prototyping-without-mutation', 'mark_test3_start', 'mark_test3_end');
// OUTPUT tests
var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
var req = items[i];
console.log(req.name + ': ' + req.duration.toFixed(2));
}

How can I create a new Person object correctly in Javascript?

I'm still struggling with this concept. I have two different Person objects, very simply:
;Person1 = (function() {
function P (fname, lname) {
P.FirstName = fname;
P.LastName = lname;
return P;
}
P.FirstName = '';
P.LastName = '';
var prName = 'private';
P.showPrivate = function() { alert(prName); };
return P;
})();
;Person2 = (function() {
var prName = 'private';
this.FirstName = '';
this.LastName = '';
this.showPrivate = function() { alert(prName); };
return function(fname, lname) {
this.FirstName = fname;
this.LastName = lname;
}
})();
And let's say I invoke them like this:
var s = new Array();
//Person1
s.push(new Person1("sal", "smith"));
s.push(new Person1("bill", "wonk"));
alert(s[0].FirstName);
alert(s[1].FirstName);
s[1].showPrivate();
//Person2
s.push(new Person2("sal", "smith"));
s.push(new Person2("bill", "wonk"));
alert(s[2].FirstName);
alert(s[3].FirstName);
s[3].showPrivate();
The Person1 set alerts "bill" twice, then alerts "private" once -- so it recognizes the showPrivate function, but the local FirstName variable gets overwritten.
The second Person2 set alerts "sal", then "bill", but it fails when the showPrivate function is called. The new keyword here works as I'd expect, but showPrivate (which I thought was a publicly exposed function within the closure) is apparently not public.
How can I ensure that my closure is a reusable object with publicly exposed methods? I am using the (function() { //object code })(); syntax to ensure my variables are scoped only to the object being created. Thanks!
this is using the technique from "JavaScript: The Good Parts" by Douglas Crockford
http://oreilly.com/catalog/9780596517748
Chapter 5: Inheritance - section 'Functional'
var personConstructor = function (spec) {
var that = {};
that.fullName = function () {
return spec.first_name + " " + spec.last_name;
};
that.setFirstName = function (firstName) {
spec.first_name = firstName;
};
return that;
};
var john = personConstructor({first_name: "John", last_name: "Doe"});
var jane = personConstructor({first_name: "Jane", last_name: "Doe"});
alert(john.fullName()); // John Doe
alert(jane.fullName()); // Jane Doe
john.first_name = "OVERWRITE";
alert(john.fullName()); // John Doe
john.setFirstName("OVERWRITE-VIA-SETTER");
alert(john.fullName()); // OVERWRITE-VIA-SETTER Doe
put this together on jsfiddle, link below if you want to test drive it,
watch out for the 4 alerts when the page loads
http://jsfiddle.net/SecKg/
I modified Person2 to declare the showPrivate function within the constructor -- ensuring the function was public:
;Person2 = (function() {
var prName = 'private';
return function(fname, lname) {
this.FirstName = fname;
this.LastName = lname;
this.showPrivate = function() { alert(prName); };
}
})();
You want to be using the prototype method of creating Person objects.
;(function(){
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
function showPrivate() {
alert(this.firstName);
}
}
Person.prototype.getName = function() {
return this.firstName + ' ' + this.lastName;
}
})();
showPrivate will be private as it's creating inside the constructor. The getName function is public, and can be invoked from outside the Person object.
I'm not entirely sure what you're trying to accomplish here. Your question is a little confusing. Perhaps try to reword?
//this would allow for instance:
// john.setName('first', 'Johnny');
// jane.setName('last', 'Smith');
var personConstructor = function (spec) {
return {
fullName: function () {
return spec.first_name + " " + spec.last_name;
},
setName: function (nameType, name) {
spec[nameType+'_name'] = name;
}
};
};

Categories