JavaScript calls to methods from within constructors - javascript

I was reading the re-introduction to JavaScript on MDN website and came across this in the Custom Objects section:
function personFullName() {
return this.first + ' ' + this.last;
}
function personFullNameReversed() {
return this.last + ', ' + this.first;
}
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = personFullName;
this.fullNameReversed = personFullNameReversed;
}
It says on the MDN website that you can make a reference to the personFullName() and personFullNameReversed() functions from within the Person constructor simply by typing in their names and assigning them as values to the two variables stated in the code above (this.fullName and this.fullNameReversed). This is all very clear to me, but my question is why are the brackets next to personFullName and personFullNameReversed omitted? Shouldn't it say:
this.fullName = personFullName();
this.fullNameReversed = personFullNameReversed();?
The way that it was presented in the example from the MDN website I feel like those fullName and fullNameReversed properties from the Person constructor are pointing to some already declared global variables instead of functions declared outside of the Person constructor.

If you add the brackets, you'll call the functions and assign their return values to this.fullName and this.fullNameReversed.
The code is referring to the functions, not calling them.

It's assigning the function, not the result of the function. It's equivalent to:
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = function () {
return this.first + ' ' + this.last;
};
this.fullNameReversed = function () {
return this.last + ', ' + this.first;
};
}
so now you can do:
var jack = new Person('Jack', 'Smith');
console.log(jack.fullName()); // Jack Smith

Related

Sharing 'this' between files

I'm working on creating an object that will have many methods, and trying to avoid my files being incredibly long. The problem is some of methods refer to other information in the object. I'd like to be able to do something like this:
index.js
var User = function(first, last){
this.firstname = first;
this.lastname = last;
};
User.prototype.name = require('./methods/name.js')
methods/name.js
module.exports = {
full: function(){
return this.firstname + " " + this.lastname;
},
formal: function(){
return "Mr. " + this.lastname;
}
};
It makes sense why this doesn't work in this situation, but is there a different solution to be able to reference the other file? The only I can think of is using fs and eval() instead of require, but that seems like a hack to me, or the obvious of have a long file. Is there something better?
I'm planning on having about 35 objects on the prototype with each having an average of 4 methods on it. Suggestions? Thanks.
The problem doesn't have anything to do with it being in separate files. You would get the same problem all in one file if you defined User like this:
var User = function(first, last){
this.firstname = first;
this.lastname = last;
};
User.prototype.name = {
full: function(){
return this.firstname + " " + this.lastname;
},
formal: function(){
return "Mr. " + this.lastname;
}
};
Since when you call someuser.name.full() the this will be bound to someuser.name not someuser.
If you don't need to namespace those functions and only did so because you were unsure how else to extend the prototype from another file, you can use Object.assign:
Object.assign( User.prototype, require('./methods/name.js') );
Then you'll be able to call someuser.full() or someuser.formal() and of course this will have the correct value.
You can bind those functions like this:
User.prototype.name = require('./methods/name').bind(this)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
Also—lose the .js in your require path.
This should keep your code modular
// index.js
var userMethods = require('.methods/name.js');
var User = function(first, last){
this.firstname = first;
this.lastname = last;
};
User.prototype.name = userMethods.full;
User.prototype.formalName = userMethods.formal;
var Abbey = new User('Abbey', 'Jack');
console.log(Abbey.firstname); // Abbey
console.log(Abbey.lastname); // Jack
console.log(Abbey.name()); // Abbey Jack
console.log(Abbey.formalName()); // Mr. Jack
// methods/name.js
module.exports = {
full: function(){
return this.firstname + " " + this.lastname;
},
formal: function(){
return "Mr. " + this.lastname;
}
};

Using apply() in IIFE

I'm trying to use apply() within IIFE. I get an error 'intermediate value is not a function' where am I going wrong ?
var Person = {
getFullName : function(firstName, lastName) {
return firstName + ' ' + lastName;
}
}
/* IIFE - using apply */
(function(firstName, lastName) {
getName = function(){
console.log("From IIFE..");
console.log(this.getFullName(firstName, lastName));
}
getName();
}).apply(Person, ['John', 'Doe']);
Plnnkr : http://plnkr.co/edit/77db8Mu4i9RXGqt26PAP?p=preview
The problem is with ;, in your code the Person object syntax is not terminated, and the IIFE is considered as continuation of that.
Read: Automatic semicolon insertion
If you look at external libraries, their IIFE statement starts with a ;, that is used to escape from this scenario.
Also note that, inside your getName function this does not refer to the Person object, you need to either use a closure variable or pass the value for this manually.
var Person = {
getFullName: function(firstName, lastName) {
return firstName + ' ' + lastName;
}
}
/* IIFE - using apply */
;
(function(firstName, lastName) {
var self = this;
var getName = function() {
console.log("From IIFE..");
console.log(self.getFullName(firstName, lastName));
}
getName();
}).apply(Person, ['John', 'Doe']);

Define getter / setter in object function

I'm learning JavaScript, and was wondering if it was possible to define getters and setters in object functions.
The main difference is the way of calling it, if defined as getFullName = function(), I should call the method as myObj.getFullName(), however, as for arrays, a getter allows for it to be called as a simple property myObj.fullName (without parenthesis).
As I saw in MDN reference (http://mzl.la/1CIUuIw), it is easily done in Object Literals:
var obj = {
get var(){
return "something";
}
}
However, I can't do it on object functions like so:
function obj(name, lname){
this.name = name;
this.lastName = lname;
get fullName(){
return this.name + " " + this.lastName;
}
}
Getting a "Unexpected identifier" error...
As get XX(){} is used for var obj = {}; But here you use a constructor to create new object. So you should use MDN - Object.defineProperty().
And if you want the fullName apply on all object create from obj, apply it on its prototype.
function obj(name, lname){
this.name = name;
this.lastName = lname;
}
Object.defineProperty(obj.prototype, 'fullName', {
get : function() {
return this.name + " " + this.lastName;
}
});
var aObj = new obj("first", 'lastN');
console.log(aObj.fullName);
UPDATE:
If you want a more straight way, and not scared to try new things, then ES2015's class notation can do it more easily:
// ES2015 - class
class obj {
constructor(name, lname) {
this.name = name;
this.lname = lname;
}
// Define getter method for fullName
get fullName() {
return this.name + " " + this.lastName;
}
}
var aObj = new obj('Billy', 'Hallow');
console.log(aObj.fullName);
Currently most browsers don't support that, if you want to use this in your site, you need to use js compilers like Babel to transpile it from ES2015 to ES5.
Babel also provide a playground for those who has interest in ES2015, you can copy above codes to the playground to see how it works.
Javascript uses prototype inheritance and its functions start with function keyword. By convention, object's first character is capitalised.
function Obj(name, lname){
this.name = name;
this.lastName = lname;
}
Obj.prototype.get = function() {
return this.name + " " + this.lastName;
}
var person = new Obj('luke', 'lim');
console.log(person.get()); // 'luke lim'
Just make a variable and assign the function to it.
function obj(name, lname){
this.name = name;
this.lastName = lname;
this.fullName = function(){
return this.name + " " + this.lastName;
};
}
Try to read something about Javascript closure if you want to know more about it.
Furthermore, this link, Javascript methods, is explaining EXACTLY what you need, which is adding a method to an object. And a getter is traditionally just a method.
function obj(name, lname){
this.name = name;
this.lastName = lname;
}
obj.prototype.getfullName = function(){
return this.name + " " + this.lastName;
}
use this as
a = new obj("My","Name");
a.getfullName() //"My Name"

How does "this" work in functions that are assigned in the constructor?

I found this example code:
function personFullName() {
return this.first + ' ' + this.last;
}
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = personFullName;
}
var dude = new Person("Michael", "Jackson");
alert(dude.fullName());
Which alerts "Michael Jackson". I changed it to call personFullName from the constructor instead of assigning the function object:
function personFullName() {
return this.first + ' ' + this.last;
}
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = personFullName();
}
var dude = new Person("Michael", "Jackson");
alert(dude.fullName);
I would expect the "fullName" property to now be a string instead of a function. But now it alerts "undefined undefined". Can anyone explain why my version doesn't work?
In JavaScript, this is typically whatever comes before the . in the function call. So the fact that you said dude.fullName() is what caused this in fullName() to be set to dude1.
In the second version in your question, you're not calling it the same way. You're calling personFullName() without anything in front of it (which is correct, since it's no longer attached to a Person object). That means that this ends up defaulting to the same value as window. Since window has no first or last properties set on it, this.first and this.last are undefined.
To fix this, you can make your person be an argument to the personFullName() function:
function personFullName(person) {
return person.first + ' ' + person.last;
}
and then call it like
…
this.fullName = personFullName(this);
1: Note that the method has to be a property on the object for the this binding to work. You can't just call object.someMethod() and get have this set to object in someMethod. In your code, the following would not work:
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = this.personFullName();
}
Uncaught TypeError: this.personFullName is not a function
Neither would this:
function personFullName() {
return this.first + ' ' + this.last;
}
function Person(first, last) {
this.first = first;
this.last = last;
}
var dude = new Person("Michael", "Jackson");
alert(dude.personFullName());
Uncaught TypeError: dude.personFullName is not a function
You can get around this restriction in any situation with the apply helper method: this.fullName = personFullName.apply(this) does what you expect the second version of your code to do and you can also call personFullName.apply(dude) at any point and get "Michael Jackson" back.
this is the window in your personFullName function as it wasn't called in the correct context. You can use apply to call it with the correct context without modifying the personFullName function.
function personFullName() {
return this.first + ' ' + this.last;
}
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = personFullName.apply(this); // The magic
}
var dude = new Person("Michael", "Jackson");
alert(dude.fullName);
A better alternative to fix that would be:
Person.prototype.personFullName = function() {
return this.first + ' ' + this.last;
}
The context in which your are accessing this in your second example, is referencing the window object. window has no fullName property set to it.
If you add alert(this); to both functions you'd see what I mean.

Correct syntax for setting object's property to return value of an object-function?

I'm learning javascript and was going through an example here:
https://developer.mozilla.org/en/A_re-introduction_to_JavaScript
function personFullName() {
return this.first + ' ' + this.last;
}
function personFullNameReversed() {
return this.last + ', ' + this.first;
}
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = personFullName;
this.fullNameReversed = personFullNameReversed;
}
var x = new Person('mickey', 'mouse');
document.write(x.fullName());
Why are the lines of code
this.fullName = personFullName;
this.fullNameReversed = personFullNameReversed;
instead of
this.fullName = personFullName();
this.fullNameReversed = personFullNameReversed();
I thought we're setting this.fullName based on the return value of personFullName()
That code is making the "fullName" and "fullNameReversed" properties be functions, not simple properties.
Thus when you want the full name you'd write x.fullName();
Functions are objects in JavaScript and can be the value of variables and properties. This is a feature that makes JavaScript surprisingly powerful.
this.fullName = personFullName;
Creates a method called fullName, and assigns it the function declared as personFullName
If you were to do
this.fullName = personFullName();
that would create an object property called fullName that held the value that personFullName() produced at that particular moment when invoked.
personFullName returns the function itself (like a pointer).
personFullName() returns the results of the function.
This allows the Person object to have a method that returns the full name, as opposed to a property. If I use this object like x.first = newVal, the fullName method re-calculates the full name.
If it were a property, I would have to use it like ''x.first = newVal; x.fullName = newFullName;'.
Hope this helps.

Categories

Resources