Javascript constructor behaviour - javascript

I have a question in respect of the js constructor function. I have the following code:
var PersonConstructorFunction = function (firstName, lastname, gender) {
this.personFirstName = firstName;
this.personLastName = lastname;
this.personGender = gender;
this.personFullName = function () {
return this.personFirstName + " " + this.personLastName;
};
this.personGreeting = function (person) {
if (this.personGender == "male") {
return "Hello Mr." + this.personFullName();
}
else if (this.personGender == "female") {
return "Hello Mrs." + this.personFullName();
}
else {
return "Hello There!";
}
};
};
var p = new PersonConstructorFunction("Donald", "Duck", "male");
p2 = new PersonConstructorFunction("Lola", "Bunney", "female");
document.write(p2.personGreeting(p2) + " ");
The result is quite obvious - --Hello Mrs. Lola Bunney--
The question is:
There are two equivalent objects p and p2 with the same number of properties and methods. I can't understand the following behaviour when I call the personGreeting method of one object and pass the second object as the argument:
**document.write(p2.personGreeting(p) + " ");**
in this case I get --Hello Mrs. Lola Bunney-- but what about the p object that is passed as the argument?
personGreeting gets the person object, determines their gender and bsed on the result shows appropriate greetings.
Resently I learned C# and constructors there works similarly I guess.

You are not doing anything with the passed parameter. Since you are utilizing this only the variables that are within your constructor are being called.
You COULD do person.personFullName(); and that would mean that the parameters member personFullName() would be called and not your constructors.

Related

Are we declaring a function as a method on a variable?

I have been learning JS on my own and completed an 8 hour course on the basics. Now I am following another course where the lecturer, as it seems to me, is creating a function using dot notation. I am not sure if this is what is happening and I am having a hard time understanding exactly what it is doing.
function Person(fName, lName) {
this.firstName = fName
this.lastName = lName
}
const person1 = new Person("Bruce", "Wayne")
const person2 = new Person("Bat", "Man")
person1.getFullName = function () {
return this.firstName + ' ' + this.lastName
}
console.log(person1.getFullName())
The part I am confused about is:
person1.getFullName = function () {
return this.firstName + ' ' + this.lastName
}
I see in the Person() function we are creating an object called person1 using "this" and a key to pass in parameters as values for those keys. However it looks to me like there is a function being assigned to the person1 object and what I am assuming is the name of the function, looks like a method on the person1 object using dot notation. Can anyone explain how this works?
The line
person1.getFullName = ...
is a normal assignment statement. It assigns the value on the right hand side to the property getFullName of the object in person1. If the property doesn't exist yet it will be created.
Functions are values (objects) like any other in JavaScript and thus they can be used everywhere were a value is expected.
So yes, person1.getFullName will contain a function and it can be called like any other function: person1.getFullName().
Objects have properties.
Properties have values.
Functions are a value.
If the value of a property is a function then we call that a method.
That's all there is to it.
function Person(fName, lName) {
this.firstName = fName
this.lastName = lName
}
const person1 = new Person("Bruce", "Wayne")
const person2 = new Person("Bat", "Man")
person1.getFullName = function () {
return this.firstName + ' ' + this.lastName
}
console.log(person1.getFullName())
Basically a new property called getFullName is created for the object person1. This new property is defined to return the first name and the lastname of the person1 object. So when you call console.log(person1.getFullName()) this will print the firstname and the lastname of the person1.

Passing parameters as arguments in OOP

I have just started to learn OOP. I am still trying to understand how everything works. I am trying to make a new function that will basically allow me to create a new object by passing the parameters of said class.
Is this even possible, and if so what am I doing wrong?
As stated, still learning so any advice will be appreciated.
class Person {
constructor(name) {
this.persName = name;
}
myName() {
return "My name is " + this.persName;
}
}
function getPers(_perName, fullName) {
_personName = new Person(fullName);
}
$(document).ready(function() {
getPers(John, "John Doe");
});
You can follow the following exapmle. Please note there is not much logic init. The example is just to show you how OOP can work. Of cause, there are many other and better ways to got with that, but for the first try, it should be good to use.
class Person {
//declare the constructor with required name values and optional age value
constructor(firstName, lastName, age = 0) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
//declare a typically setter, e.g. for age
setAge(age) {
this.age = age;
}
//declare a getter for the person with age
getPerson() {
return this.firstName + ' ' + this.lastName + ' is ' + this.age + ' years old.';
}
}
//here you got with some function within you need to define a new person
function getCreatedPerson(firstName, lastName, age = 0) {
//define the person with required firstname and lastname
const person = new Person(firstName, lastName);
//use the setter to set age of person
person.setAge(age);
//return the person by using the person getter
return person.getPerson();
}
//here you can call the createdPerson function
$(document).ready(function() {
console.log(getCreatedPerson('John', 'Doe', 32));
});
Hope it helps a bit to understand how it could work.
Yes, you can take the fullname as a parameter to your function and pass it through to the OOP method or constructor you're calling.
But you can't take a "reference to a variable" as an argument, like you tried with _perName/_personName. Instead, use the return keyword to return the created object:
function createPerson(fullName) {
return new Person(fullName);
}
$(document).ready(function() {
var john = createPerson("John Doe");
… // use john
});

JavaScript class notation and the 'this' keyword

I'm aware there have been a few other questions on this topic, but none of them seem to give a conclusive answer.
I'm building a HTML/CSS/JS mobile app, and have been trying the following style of notation to define some classes:
Style A
var Thing = (function ()
{
var _instance;
var _firstName;
var _lastName;
function Thing(firstName, lastName)
{
_instance = this;
_firstName = firstName;
_lastName = lastName;
}
Thing.prototype.getMyName = function ()
{
return _firstName + " " + _lastName;
}
Thing.prototype.speak = function ()
{
return ("My name is " + _instance.getMyName());
}
return Thing;
}());
The advantages of this are:
Member variables are encapsulated and can be referred to directly (e.g. without the this prefix).
I can use the _instance variable and therefore avoid ambiguity around the identity of this.
The notation is reasonably clean and readable.
I also gave the following alternatives a try:
Style B
function Thing(firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}
Thing.prototype.getMyName = function()
{
return this._firstName + " " + this._lastName;
};
Thing.prototype.speak = function()
{
return "My name is " + this.getMyName();
};
Style C
class Thing
{
constructor (firstName, lastName)
{
this._firstName = firstName;
this._lastName = lastName;
}
getMyName ()
{
return this._firstName + " " + this._lastName;
}
speak ()
{
return ("My name is " + this.getMyName());
}
}
But despite their advantages, I have found B and C difficult to work with because of problems associated with the this keyword; that is, depending on the context of the caller this can refer to different things within the class methods. Furthermore in both these cases, using an _instance variable as I have in A is not possible because all members need to prefixed with this..
However, as pointed out in the comments, Style A does not work when multiple instances of the class are created.
What's the best way to write such classes but avoid problems with this?
If you want to avoid prototype methods with late-bound this at all costs, you can use the following style in ES6:
class Thing {
constructor(firstName, lastName) {
this.getMyName = () => firstName + " " + lastName;
this.speak = () => "My name is " + this.getMyName();
}
}
(you can also use a function declaration, but the class has the advantage that it prevents calls without new automatically)
You are creating a closure and then sharing the closure among instantiated objects by the Thingconstructor. It will not work as intended.
var elton = new Thing("Elton", "Johnnavartangula");
elton.getMyName(); // <- "Elton Johnnavartangula"
var fenry = new Thing("Fenry", "Honda");
elton.speak(); // <- "My name is Fenry Honda"
sharing privates among instantiated objects is another topic and can be done in several ways like in one of my previous answer or like
function Source(){
var priv = "secret";
return {gp : function(){return priv},
sp : function(v){priv = v}
}
}
sharedProto = Source(); // priv is now under closure to be shared
var p1 = Object.create(sharedProto); // sharedProto becomes o1.__proto__
var p2 = Object.create(sharedProto); // sharedProto becomes o2.__proto__

Return new object it built in Javascript

I am practicing Javascript object function. Supposed I have firstName and lastName as two arguments of my function.I want to display like this {"firstName":"tim","lastName":doe} . Here is my code but it printed out undefined. Any idea? Thank you!
function myFunction(firstName, lastName) {
this.name1 = firstName;
this.name2 = lastName;
}
var obj = new myFunction();
console.log(myFunction('tim', 'doe'));
Try this:
console.log(new myFunction('tim', 'doe'));
Or this:
console.log(obj);
You can try this
function myFunction(firstName, lastName) {
this.name1 = firstName;
this.name2 = lastName;
}
var obj = new myFunction('tim', 'doe');
console.log(obj);
You can see this documentation JavaScript Constructors
This kind of function called constructor, and you shouldn't call it directly. You have to use it with new.
console.log(new myFunction('tim', 'doe'));
This will print the result as you expect.
To distinguish the constructors from normal functions, it's better to name it begin with capital letter, like this:
function MyFunction(...) {...}
the undefined you receive is from the function not having a return value, see this post regarding that:Simple function returning 'undefined' value
to get the result you want...
function myFunction(firstName, lastName) {
this.name1 = firstName;
this.name2 = lastName;
}
var obj = new myFunction('tim', 'doe');
console.log(obj);
Let's explore what this line does: console.log(myFunction('tim', 'doe'));
This part: myFunction('tim', 'doe') executes myFunction as a function. Since myFunction does not have a return operator, it's return value is 'undefined' which is javascript's way of saying it doesn't exist. Thus, the word 'undefined' is printed on the console.
Additional tips:
Try adding this line: console.log(typeof myFunction);
This should print 'function'.
(May the 'typeof' operator become your best friend)
Try adding a return line as the last line of myFunctions such as:
return 'First name: ' + firstName + " Last name: " + lastName;
However, at this point the 'var obj = new myFunction();' line is unused.
Try adding another line:
console.log(typeof obj);
This should print 'object' which means that 'obj' is just that - an object.
Here is a complete example you can play with:
function myFunction(firstName, lastName) {
this.name1 = firstName;
this.name2 = lastName;
this.getNames = function() {
return 'First name: ' + firstName + " Last name: " + lastName;
}
console.log("This executes once upon instatiation (the line with var obj = new ...)");
return "Return value";
}
var obj = new myFunction('tim', 'doe');
console.log(typeof myFunction);
console.log(typeof obj);
console.log(obj.getNames());
Let me know if any of the above needs clarification. Good luck ...
BTW, this is what the output should look like on the console:
This executes once upon instatiation (the line with var obj = new ...)
script.js:14 function
script.js:15 object
script.js:16 First name: tim Last name: doe

How to add methods to a (JSON) object's prototype?

Let's say I receive some JSON object from my server, e.g. some data for a Person object:
{firstName: "Bjarne", lastName: "Fisk"}
Now, I want some methods on top of those data, e.g. for calculating the fullName:
fullName: function() { return this.firstName + " " + this.lastName; }
So that I can
var personData = {firstName: "Bjarne", lastName: "Fisk"};
var person = PROFIT(personData);
person.fullName(); // => "Bjarne Fisk"
What I basically would want to do here, is to add a method to the object's prototype. The fullName() method is general, so should not be added to the data object itself. Like..:
personData.fullName = function() { return this.firstName + " " + this.lastName; }
... would cause a lot of redundancy; and arguably "pollute" the data object.
What is the current best-practice way of adding such methods to a simple data object?
EDIT:
Slightly off topic, but if the problem above can be solved, it would be possible to do some nice pseudo-pattern matching like this:
if ( p = Person(data) ) {
console.log(p.fullName());
} else if ( d = Dog(data) ) {
console.log("I'm a dog lol. Hear me bark: "+d.bark());
} else {
throw new Exception("Shitty object");
}
Person and Dog will add the methods if the data object has the right attributes. If not, return falsy (ie. data does not match/conform).
BONUS QUESTION: Does anyone know of a library that either uses or enables this (ie makes it easy)? Is it already a javascript pattern? If so, what is it called; and do you have a link that elaborates? Thanks :)
Assuming your Object comes from some JSON library that parses the server output to generate an Object, it will not in general have anything particular in its prototype ; and two objects generated for different server responses will not share a prototype chain (besides Object.prototype, of course ;) )
If you control all the places where a "Person" is created from JSON, you could do things the other way round : create an "empty" Person object (with a method like fullName in its prototype), and extend it with the object generated from the JSON (using $.extend, _.extend, or something similar).
var p = { first : "John", last : "Doe"};
function Person(data) {
_.extend(this, data);
}
Person.prototype.fullName = function() {
return this.first + " " + this.last;
}
console.debug(new Person(p).fullName());
There is another possibility here. JSON.parse accepts a second parameter, which is a function used to revive the objects encountered, from the leaf nodes out to the root node. So if you can recognize your types based on their intrinsic properties, you can construct them in a reviver function. Here's a very simple example of doing so:
var MultiReviver = function(types) {
// todo: error checking: types must be an array, and each element
// must have appropriate `test` and `deserialize` functions
return function(key, value) {
var type;
for (var i = 0; i < types.length; i++) {
type = types[i];
if (type.test(value)) {
return type.deserialize(value);
}
}
return value;
};
};
var Person = function(first, last) {
this.firstName = first;
this.lastName = last;
};
Person.prototype.fullName = function() {
return this.firstName + " " + this.lastName;
};
Person.prototype.toString = function() {return "Person: " + this.fullName();};
Person.test = function(value) {
return typeof value.firstName == "string" &&
typeof value.lastName == "string";
};
Person.deserialize = function(obj) {
return new Person(obj.firstName, obj.lastName);
};
var Dog = function(breed, name) {
this.breed = breed;
this.name = name;
}
Dog.prototype.species = "canine";
Dog.prototype.toString = function() {
return this.breed + " named " + this.name;
};
Dog.test = function(value) {return value.species === "canine";};
Dog.deserialize = function(obj) {return new Dog(obj.breed, obj.name);};
var reviver = new MultiReviver([Person, Dog]);
var text = '[{"firstName": "John", "lastName": "Doe"},' +
'{"firstName": "Jane", "lastName": "Doe"},' +
'{"firstName": "Junior", "lastName": "Doe"},' +
'{"species": "canine", "breed": "Poodle", "name": "Puzzle"},' +
'{"species": "canine", "breed": "Wolfhound", "name": "BJ"}]';
var family = JSON.parse(text, reviver)
family.join("\n");
// Person: John Doe
// Person: Jane Doe
// Person: Junior Doe
// Poodle named Puzzle
// Wolfhound named BJ
This depends on you being able to unambiguously recognizing your types. For instance, if there were some other type, even a subtype of Person, which also had firstName and lastName properties, this would not work. But it might cover some needs.
If you're dealing with plain JSON data then the prototype of each person object would simply be Object.prototype. In order to make it into an object with a prototype of Person.prototype you'd first of all need a Person constructor and prototype (assuming you're doing Javascript OOP in the traditional way):
function Person() {
this.firstName = null;
this.lastName = null;
}
Person.prototype.fullName = function() { return this.firstName + " " + this.lastName; }
Then you'd need a way to turn a plain object into a Person object, e.g. if you had a function called mixin which simply copied all properties from one object to another, you could do this:
//example JSON object
var jsonPerson = {firstName: "Bjarne", lastName: "Fisk"};
var person = new Person();
mixin(person, jsonPerson);
This is just one way of solving the problem but should hopefully give you some ideas.
Update: Now that Object.assign() is available in modern browsers, you could use that instead of writing your own mixin function. There's also a shim to make Object.assign() work on older browsers; see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill.
You should probably not do this.
JSON allows you to serialize a state, not a type. So in your use case, you should do something like this :
var Person = function ( data ) {
if ( data ) {
this.firstName = data.firstName;
this.lastName = data.lastName;
}
};
Person.prototype.fullName = function ( ) {
return this.firstName + ' ' + this.lastName;
};
//
var input = '{"firstName":"john", "lastName":"Doe"}';
var myData = JSON.parse( input );
var person = new Person( myData );
In other words you want to change prototype (a.k.a. class) of existing object.
Technically you can do it this way:
var Person = {
function fullName() { return this.firstName + " " + this.lastName; }
};
// that is your PROFIT function body:
personData.__proto__ = Person ;
After that if you will get true on personData instanceof Person
Use the new-ish Object.setPrototypeOf(). (It is supported by IE11 and all the other browsers now.)
You could create a class/prototype that included the methods you want, such as your fullName(), and then
Object.setPrototypeOf( personData, Person.prototype );
As the warning (on MDN page linked above) suggests, this function is not to be used lightly, but that makes sense when you are changing the prototype of an existing object, and that is what you seem to be after.
I don't think it is common to transport methods with data, but it seems like a great idea.
This project allows you to encode the functions along with your data, but it is not considered standard, and requires decoding with the same library of course.
https://github.com/josipk/json-plus
Anonymous objects don't have a prototype. Why not just have this:
function fullName(obj) {
return obj.firstName + ' ' + obj.lastName;
}
fullName(person);
If you absolutely must use a method call instead of a function call, you can always do something similar, but with an object.
var Person = function (person) { this.person = person; }
Person.prototype.fullName = function () {
return this.person.firstName + ' ' + this.person.lastName;
}
var person = new Person(personData);
person.fullName();
You don't need to use prototypes in order to bind a custom method in your barebone object.
Here you have an elegant example that don't pollute your code avoiding redundant code
var myobj = {
title: 'example',
assets:
{
resources: ['zero', 'one', 'two']
}
}
var myfunc = function(index)
{
console.log(this.resources[index]);
}
myobj.assets.giveme = myfunc
myobj.assets.giveme(1);
Example available in https://jsfiddle.net/bmde6L0r/

Categories