Can I edit a constructor function in javaScript to add properties? - javascript

I have this basic constructor function(with two inputs)
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; }
To create an object with the constructor function Person, I do
var person1 = new Person("John", "Doe");
the output is
if I want to add a new property to the object person1 (age, for example), I do
person1.age = 20;
and the output is
now, I want to create a new object with the same constructor function... let say person2. Because I know his age, I want to add this info to the object too. I do it like this
var person2 = new Person("Jennifer", "Unknown", 30);
the output is
the output comes without the age property, because the constructor function Person doesn't know it
To create the object person2 with those 3 inputs using Person, I must redefine Person (with those 3 inputs) and recreate person2, passing those 3 info's or add the extra info if object already exists ...
My question is:
How to proceed, if I want to add a third object with Person (person3), by passing 4 inputs (firstName, lastName, age, nationality)? I have to redefine Person (again) and then create person3? ... This seems tedious and not logic work! I confess I'am a newbie in jS!
is it there a way to edit a constructor function without redefining it every time I want to create a new object with extra properties? Can I add new property to a constructor functions without redefining it? And, if I want to edit a constructor function to add a method? for example:
status = function(){
alert(this.firstName + " " + this. lastName + " " + ", a person with" + " " + this. age + " " + "years old" + " " + "was found nearby doing jS stuff!" )}
summarizing... my goal is:
I start with start and want to end with end without redefining Person, just editing it.
My question make sense? I appreciate any tip.

Well, there's no such thing as "edit" a function, but you can reuse the old implementation to define your new function instead of rewriting it completely.
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
Person.prototype.speak = function () {
console.log('hey!')
}
p1 = new Person('first1', 'last1')
console.log('p1 firstName', p1.firstName)
p1.speak() // hey!
// store old prototype object
oldPrototype = Person.prototype
Person = function (firstName, lastName, age) {
// call the old constructor to initialize with existing properties
oldPrototype.constructor.call(this, firstName, lastName)
// assign new properties
this.age = age
// assign a new default property
this.otherProp = 'new prop'
}
// copy over the old prototype to the new Person
Person.prototype = oldPrototype
p2 = new Person('first2', 'last2', 10)
console.log('p2 firstName', p2.firstName)
console.log('p2 new properties', p2.age, p2.otherProp) // 10 and 'new prop'
p2.speak() // should work! because we already copied the old prototype object over
// it will not affect any of the old Person instances that were initialized before
console.log('p1 age', p1.age) // undefined
console.log('p1 otherProp', p1.otherProp) // undefined
While this is possible, don't overuse it!
Because it seems like an anti-pattern to override a constructor function.
You should better create a new constructor function by extending your Person instead.
Read more about Inheritance in JavaScript

Related

Object creation and use of the prototype delegation

In the following example i have a simple Spy object. I wanted to create another object setting the prototype with the original object, so I've used Object.create().
Now I have a new object, which have just some properties from the original ('code' and 'breath' method). All the other properties (objects - 'name' and arrays - 'enemies') are in the _proto_ object, which i can use, because they are delegated to the original object. So far so good.
The tricky part is that if I change anything in the AnotherSpy included in the _proto_ object (the object name for example), those changes will be reflected in all the objects created from the original spy, including himself!
I also tried create a new object with using JSON.parse(), but in this way I have a new object which only have access to the 2 things that were previously in the _proto_ object - the array of enemies and the name object, without being able to use any methods of the original object (the 'breath' method).
let Spy = {
code: '007',
enemies: ['Dr.No'],
fullName: {
firstName: 'James',
lastName: 'Bond'
},
breath: function() {
console.log('im breathing..')
}
}
// original Spy breathing
Spy.breath(); // ok, he breaths
// create a new object with Object.create()
let OtherSpy = Object.create(Spy);
console.log(OtherSpy) // have direct access to properties 'code' and function 'breath' and all the others throught the __proto__ object
// Make OtherSpy breath
OtherSpy.breath(); // ok, he is breathing
// so far so good. Lets change the property and function on the OtherSpy
OtherSpy.code = '008';
OtherSpy.breath = () => {
console.log('im a new breathing')
};
OtherSpy.breath(); // ok, he's breathing differently
console.log(Spy.code); // 007 ok, original spy has the same code
Spy.breath() // ok, he stills breath in the same way.
// change the object 'name' of the OtherSpy
OtherSpy.fullName.firstName = 'Enemy';
// That change will reflect also on the original Spy...
console.log(Spy.fullName.firstName); // Enemy !!!!
// Trying in another way:
let NewSpy = JSON.parse(JSON.stringify(Spy));
console.log('NewSpy')
console.log(NewSpy) // now i dont have access to methods in the original object
NewSpy.breath() // Uncaught TypeError: NewSpy.breath is not a function
It seems that all properties included in the _proto_ object are shared in all objects that use that prototype chain.
Aside from this tricky parts that would greatly appreciate an explanation, I would like to know the proper way to create an object in JavaScript (without using ES6 classes) in order to get the advantage of the prototype delegation
and to be able to modify the properties and functions of the derived object without messing up with the original object nor any other derived objects.
Thanks in advance!
Nested properties are somewhat unuseful, so you may flatten it through using getters/setters:
const Spy = {
firstName: "Agent",
lastName: "Unnamed",
breath(){
console.log(`${this.fullName} is breathing`);
},
get fullName(){
return this.firstName + " " + this.lastName;
},
set fullName(name){
const [first, last] = name.split(" ");
this.firstName = first;
this.lastName = last;
}
};
const james = Object.create(Spy);
james.fullName = "James Bond";
james.breath();
console.log(james.fullName, james.firstName, james.lastName);
Another way would be to construct the name object inside of an constructor:
function Spy(name, code, enemies){
this.name = (([first, last]) => ({first, last}))(name.split(" "));
this.name.toString = () => name;
this.code = code;
this.enemies = enemies;
}
Spy.prototype = {
breath(){
console.log(`${this.name} is breathing`);
}
}
Usable as:
const james = new Spy("James Bond", "007", ["Dr. No"]);
james.breath();
console.log(james.name, "" + james.name);

value of parent object not changed from inheritance through prototype

Am new in javascript OOP, please bear with me
Changing the value of parent object from inherited object Student should change the age of the person but am getting exactly same value.
<script>
function Person(age){
this.age=age;
}
function Student(){}
var person=Student.prototype=new Person(10);
var oldAge=person.age;
Student.age=20;
var newAge=person.age;
alert("Person old age="+oldAge+"\New age="+newAge);
</script>
as person and Student inherited from same Person object then value of age of both sudent and person should change on changing the value from Student
I already went through Prototypical inheritance - writing up and JavaScript Inherited Properties Default Value questions
problem is i want to change the value of Person through Student which inherits the property of Person.
I think am missing something here please help me understand this.
There are two patterns which are used to implement inheritance in javascript
Prototype Object oriented pattern
Constructor Object oriented pattern
Now i will use the first method
Some prerequisite knowledge :
All the JS objects have a property that points to a prototype
object, thus apart from it's own properties, the object can also
access porperties of it's own prototype
__proto__ : It's a property which all objects have, this points to
the prototype of that object.
Object.create(arg) : It is used to create objects and initaliaze
their prototype OR set their __proto__ property.
Object.create MDN link
below snippet implements inheritance as well as allows you to modify the value of Person through Student.
:
function Person(age){
this.age=age;
this.getAge = function () { return this.age;}
};
function Student(){};
//Creating Person instance
var person = new Person(23);
console.log("Old Person age is " + person.age);
//Creating a student instance and inheriting it from person instance
//Object.create method creates a object whose __proto__ point to the object passed
//Thus student will be an object having a property __proto__ that would point to person instance
//This assosciation allows the instance of student to access insatnce of Person
var student = Object.create(person);
//Change age of person through student
student.__proto__.age = 24;
console.log("New Person age is " + person.age);
console.log("We can also call parent object methods from child" + " for e.g calling getAge from student" + student.getAge());
Now to achieve something similar using second method, following snippet can be used :
function Person(age){
this.age=age;
}
function Student(){}
//Create person instance
var person = new Person(23);
console.log("Old age of person is " + person.age);
//Inherit the person instance
Student.prototype = person;
//create a student object
var student = new Student();
//Change the person instance age value
//this change is possible because we
//can access person object through
//student.__proto__.
student.__proto__.age = 24;
console.log("New age of person is " + person.age);
The simplest way to explain this is that person.age is a property on an instance, where as Student.age is a static property that is not related to your instance.
You can simplify the whole example to remove student, and you'll still see you have something like an instance property and a static property.
function Person(age){
this.age = age;
}
var person = new Person(10);
var oldAge = person.age;
Person.age = 20;
var newAge = person.age;
alert("Person old age=" + oldAge + "\New age=" + newAge);
alert(Person.age);
In JavaScript, you should always use prototypal inheritance to make this work.
var person = {
age: 10
}
var student = Object.create(person);
var oldAge=person.age;
student.age=20;
var newAge=student.age;
alert("Person old age="+oldAge+"\New age="+newAge);
In your code, Since function Student doesn't have the property age in Creation phase, JavaScript engine will make a property called age in memory. In execution phase, JavaScript engine will assign 20 to the new property that was created in creation phase.
If you execute inside the browser, the you will notice function Student has a new property, called age, which is equal to 20.

Usecase for Classical vs Prototypical

I have a question in regards to classical inheritance vs prototypical inheritance. I wanted to see what is better?
Let's say we have a function called familyTree.
function familyTree(){
this.lastname = "xyz";
}
If i want to add any additional details for them as far as I read, we can inherit parent in two ways:
1: prototypical way:
familyTree.prototype.personDetails = function(){
this.firstName = "abc";
this.middleName = "middle1";
var newToString = function(name){ //overwriting toString method
console.log("The name is: "+name+"Middle Name is "+middleName);
}
}
2: Classical way using 'new' keyword
var newPerson = new familyTree();
newPerson.firstName = "abc";
newPerson.middleName = "middle1";
newperson.newToString = function (name){
console.log("The name is: "+name+"Middle Name is "+middleName);
}
Let's say if I want to create 100 different middle names.
What makes more sense? Using Classical Inheritance or Prototypical? Because using Classical can copy over all the objects but using prototypical can get everything messy.
Please illustrate, when one should be using classical vs prototypical.
There is only one kind of inheritance in JavaScript and that is prototypical inheritance. "Classical" inheritance doesn't exist in JavaScript.
Despite the "syntactic sugar" that the JavaScript language has to make OOP developers who are very comfortable with class-based programming feel at home (including the class keyword), JavaScript doesn't actually have or use classes. That language vocabulary is just meant to make you feel all warm and fuzzy.
Your question is not actually asking about inheritance, it's asking about whether properties should be attached to the constructor function or the prototype.
See the code comments for explanations:
// When you create a function that you will use to create object instances,
// you have a "constructor function". By convention, these functions should
// be named in PascalCase as a way to let others know that they are to be
// used in conjunction with the "new" keyword as a constructor.
function FamilyTree(first, middle, last){
// Because this function will be used to create instances of a
// FamilyTree object, each instance created will need to store
// data that is different from another. This is done with "instance
// properties" and they are created by prepending the property name
// with "this.". The "this" object will be referenced by the object
// instance variable that is used when the instance is created:
this.firstName = first;
this.middleName = middle;
this.lastName = last;
}
// JavaScript objects don't technically have "methods" - - they
// have properties that store functions and functions are how
// to add behavior to an object. Since the behaviors of an object
// don't typically change from instance to instance, you should not
// add them to the constructor function. If you did, the code would
// work, but each instance would need to store a copy of the exact
// same behavior, making the objects unnecessarialy large in memory.
// Instead, we attach behaviors that all instances of an object will
// need to the prototype of the constructor and that way all instances
// created from the constructor will inherit the behaviors, but the
// acutal behavior will only be stored once, thus saving on memory space
// and eliminating the possibility of one instance behaving differently
// than others, unintentionally.
// Implementing "methods" that all FamilyTree instances will inherit:
FamilyTree.prototype.newToString = function(name){
return "First name: " + this.firstName + ", Last Name: " + this.lastName;
}
// The constructor function's prototype (as with all objects) derives from "Object"
// which defines a "toString" property, by re-defining that property on the constructor's
// prorotype, we will be able to override the inherited one
FamilyTree.prototype.toString = function(name){
return this.lastName + ", " + this.firstName;
}
// To use this object, we have a few choices, but the simplest one is to just instantiate it:
var myFamilyTree = new FamilyTree("John","Fitzgerald","Kennedy");
// Now, we just work with the instance:
console.log(myFamilyTree.firstName);
console.log(myFamilyTree.middleName);
console.log(myFamilyTree.lastName);
console.log(myFamilyTree.newToString());
console.log(myFamilyTree.toString());
Now, the above code works, but is technically not organized well since the object is a "Family Tree" and all it really stores is a single person's name and some ways to output that name. Not much of a family tree. In reality, this "Family Tree" object should be made up from other objects (i.e. many individual people should be able to be included along with other pertinent family tree data). If we apply the OOP "Single Responsibility Principle" ("A class/module should have only one reason to change"), we would need to make the Family Tree object have smaller object parts. All this would involve is making the original object have a property that stores an array of people objects:
// First, make objects that represent the parts of the whole
function Person(first, middle, last, dob, maiden){
// Instance properties are added to the constructor, which makes individual instances:
this.firstName = first;
this.middleName = middle;
this.lastName = last;
this.dob = dob;
this.maidenName = maiden;
}
// Behavior properties are added to the constructor's prototype to avoid duplication
// of code across instances:
Person.prototype.newToString = function(name){
return "First name: " + this.firstName + ", Last Name: " + this.lastName;
}
Person.prototype.toString = function(name){
return this.lastName + ", " + this.firstName;
}
// Then create an object that becomes the sum of the parts:
function FamilyTree(){
// We just need a way to store family members.
// Each FamilyTree instance can have different members, so an instance property
// is needed:
this.people = [];
}
// And, again, behaviors are added to the prototype:
FamilyTree.prototype.getMemberCount = function(){
return this.people.length;
}
FamilyTree.prototype.addMember = function(personObject){
this.people.push(personObject);
}
FamilyTree.prototype.removeMember = function(personObject){
var index = this.people.findIndex(function(element){
return personObject === element;
});
this.people.splice(index, 1);
}
// And, because the tree stores an array, we can looop through it:
FamilyTree.prototype.enumerate = function(){
var result = "";
this.people.forEach(function(person){
result += person.firstName + " " + person.middleName + " " + person.lastName +
" (" + person.newToString() + " [" + person.toString() + "])";
});
return result;
};
// Now, to use the Family Tree, we first need some people
var jack = new Person("John","Fitzgerald","Kennedy", new Date(1917, 4, 29));
var bobby = new Person("Robert", "Francis", "Kennedy", new Date(1925, 11, 20));
var teddy = new Person("Edward","Moore","Kennedy", new Date(1932, 1, 22));
// Now, we add those objects to a new Family Tree instance:
var kennedyTree = new FamilyTree();
kennedyTree.addMember(jack);
kennedyTree.addMember(bobby);
kennedyTree.addMember(teddy);
console.log("The tree contains: " + kennedyTree.getMemberCount() + " members.");
console.log(kennedyTree.enumerate());
// Let's remove a member:
kennedyTree.removeMember(bobby);
console.log("The tree contains: " + kennedyTree.getMemberCount() + " members.");
console.log(kennedyTree.enumerate());

Memory usage for JavaScript object literal notation vs. constructor functions

declaring an object using literal notation like this:
var person = {
name: "",
gender: "",
age: 0
}
vs. a Constructor function like this:
var person = function(name, gender, age)
{
this.name = name;
this.gender = gender;
this.age = age;
}
First question:
When declared like this do they both take an equal amount of memory even though they're not 'instantiated' yet? (or does this concept not apply to JavaScript)
Second question:
Can both of these correctly be newed up as follows:
var john = new person();
To avoid confusion, let's use different names:
// An object we might use as a prototype
var person = {
name: "",
gender: "",
age: 0
};
// A constructor function, note the capital P
var Person = function(name, gender, age)
{
this.name = name;
this.gender = gender;
this.age = age;
};
When declared like this do they both take an equal amount of memory even though they're not 'instantiated' yet? (or does this concept not apply to JavaScript)
No. person (lower case) is a simple object with three properties. Person (capitalized) is a function object, which has an associated (blank) prototype object (Person.prototype). So in theory, the function will take up more memory than the object, because we have the function object, its associated code, and the simple object (its prototype).
It's not likely to matter, though. Function objects in and of themselves don't take up a lot of memory, that code is small, and blank objects (the prototype) take up very, very little. Presumably you're not going to have millions and millions of these, as (if I'm understanding the point of your question correctly) they're meant to be the basis of other objects.
Can both of these correctly be newed up as follows:
var john = new person();
Not literally, no. But you can create instances based on each of them. To create a new instance backed by person, you'd use Object.create; to create a new instance via Person and backed by Person.prototype, you'd use new:
// Using the `person`
var john = Object.create(person);
john.name = "John";
john.gender = "M";
john.age = 47;
// Using `Person` (the constructor function)
var mary = new Person("Mary", "F", 32);
This really only becomes interesting when at least one of the prototype properties isn't set by construction. That property might be anything, but let's take the common case: A function:
// An object we might use as a prototype
var person = {
name: "",
gender: "",
age: 0,
sayName: function() {
console.log("My name is " + this.name);
}
};
// A constructor function, note the capital P
var Person = function(name, gender, age)
{
this.name = name;
this.gender = gender;
this.age = age;
};
Person.prototype.sayName = function() {
console.log("My name is " + this.name);
};
Then:
// Using the `person`
var john = Object.create(person);
john.name = "John";
john.gender = "M";
john.age = 47;
john.sayName(); // "My name is John"
// Using `Person` (the constructor function)
var mary = new Person("Mary", "F", 32);
mary.sayName(); // "My name is Mary"
john gets sayName from person, its prototype; mary gets sayName from Person.prototype, its prototype.

In Javascript, the difference between 'Object.create' and 'new'

I think the difference has clicked in my head, but I'd just like to be sure.
On the Douglas Crockford page Prototypal Inheritance in JavaScript, he says
In a prototypal system, objects inherit from objects. JavaScript,
however, lacks an operator that performs that operation. Instead it
has a new operator, such that new f() produces a new object that
inherits from f.prototype.
I didn't really understand what he was trying to say in that sentence so I performed some tests. It seems to me that the key difference is that if I create an object based on another object in a pure prototypal system, then all the parent parent members should be on the prototype of the new object, not on the new object itself.
Here's the test:
var Person = function(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.toString = function(){return this.name + ', ' + this.age};
// The old way...
var jim = new Person("Jim",13);
for (n in jim) {
if (jim.hasOwnProperty(n)) {
console.log(n);
}
}
// This will output 'name' and 'age'.
// The pure way...
var tim = Object.create(new Person("Tim",14));
for (n in tim) {
if (tim.hasOwnProperty(n)) {
console.log(n);
}
}
// This will output nothing because all the members belong to the prototype.
// If I remove the hasOwnProperty check then 'name' and 'age' will be output.
Is my understanding correct that the difference only becomes apparent when testing for members on the object itself?
Your assumptions are correct, but there is another pattern that Douglas doesn't talk much about - the prototype can be used for properties as well. Your person class could have been written as:
var Person = function(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.name = null; //default value if you don't init in ctor
Person.prototype.age = null;
Person.prototype.gender = "male";
Person.prototype.toString = function(){return this.name + ', ' + this.age;};
In this case, iterating over properties of an instance of this class, as you do in your example, would generate no output for the 'gender' property.
EDIT 1:
The assignment of name and age in the constructor do make the properties visible by hasOwnProperty (thanks #matt for reminding me of this). The unassigned gender property would not be visible until someone sets it on the instance.
EDIT 2:
To further add to this, I present an alternative inheritance pattern - one that I have personally used for very large projects:
var inherits = function(childCtor, parentCtor) {
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.superclass = parentCtor.prototype;
childCtor.prototype = new tempCtor();
childCtor.prototype.constructor = childCtor;
};
var Person = function(name){
this.name = name;
}
Person.prototype.name = "";
Person.prototype.toString = function(){
return "My name is " + this.name;
}
var OldPerson = function(name, age){
OldPerson.superclass.constructor.call(this);
this.age = age
};
inherits(OldPerson, Person);
OldPerson.prototype.age = 0;
OldPerson.prototype.toString = function(){
var oldString = OldPerson.superclass.toString.call(this);
return oldString + " and my age is " + this.age;
}
This is a fairly common pattern with a small twist - the parent class is attached to the child via the "superclass" property permitting you to access methods/properties overridden by the child. Technically, you could replace OldPerson.superclass with Person, however that is not ideal. If you ever changed OldPerson to inherit from a class other than Person, you would have to update all references to Person as well.
EDIT 3:
Just to bring this full circle, here is a version of the "inherits" function which takes advantage of Object.create and functions exactly the same as I previously described:
var inherits = function(childCtor, parentCtor) {
childCtor.prototype = Object.create(parentCtor.prototype);
childCtor.superclass = parentCtor.prototype;
};
EDIT: This answer was originally a response to #jordancpaul's answer, which he has since corrected. I will leave the portion of my answer that helps explain the important difference between prototype properties and instance properties:
In some cases, properties are shared between all instances and you need to be very careful whenever you're declaring properties on the prototype. Consider this example:
Person.prototype.favoriteColors = []; //Do not do this!
Now, if you create a new Person instance using either Object.create or new, it doesn't work as you might expect...
var jim = new Person("Jim",13);
jim.favoriteColors.push('red');
var tim = new Person("Tim",14);
tim.favoriteColors.push('blue');
console.log(tim.favoriteColors); //outputs an array containing red AND blue!
This doesn't mean you can't ever declare properties on the prototype, but if you do, you and every developer who works on your code needs to be aware of this pitfall. In a case like this, if you prefer declaring properties on the prototype for whatever reason, you could do:
Person.prototype.favoriteColors = null
And initialize it to an empty array in the constructor:
var Person = function(name, age) {
...
this.favoriteColors = [];
}
The general rule when using this method is that default values for simple literal properties (strings, numbers, booleans) can be set on the prototype directly, but any property that inherits from Object (including arrays and dates) should be set to null and then initialized in the constructor.
The safer way is to only declare methods on the prototype, and always declare properties in the constructor.
Anyway, the question was about Object.create...
The first argument passed to Object.create is set as the prototype of the new instance. A better usage would be:
var person = {
initialize: function(name, age) {
this.name = name;
this.age = age;
return this;
},
toString: function() {
return this.name + ', ' + this.age;
}
};
var tim = Object.create(person).initialize("Tim",14);
Now the output will be the same as in your first example.
As you can see, it's a different philosophical approach from the more classical style of OOP in Javascript. With Object.create, the emphasis is on creating new objects from existing objects, rather than on the constructor. Initialization then becomes a separate step.
Personally I have mixed feelings about the Object.create approach; it's very nice for inheritance because of the second parameter that you can use to add additional properties to an existing prototype, but it also is more verbose and makes it so instanceof checks no longer work (the alternative in this example would be to check person.isPrototypeOf(tim)).
The main reason I say Object.create is verbose is because of the second parameter, but there are some useful libraries out there that address that:
https://github.com/Gozala/selfish
https://github.com/Raynos/pd
(and others)
I hope that was more enlightening than confusing!

Categories