i'm pretty new to js. i'm sorry if this sounds dumb.
but why does the following code return "undefined"
function NewPerson(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
this.getName = function(){
//alert(this.name);
alert("The age is "+this.age);
};
}
var obj1 = new NewPerson("Mark",25,"Male");
alert("The age is as follows "+obj1.getName());
// output:
The age is 25
The age is as follows undefined
Because you don't return anything.
You have to explixitly return in you function
Following should work
function NewPerson(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
this.getName = function(){
//alert(this.name);
alert("The age is "+this.age);
return this.age
};
}
var obj1 = new NewPerson("Mark",25,"Male");
alert("The age is as follows "+obj1.getName());
No one else mentioned it so I will - it is more efficient to put functions that will be the same for all instances on the constructor's prototype. Then you only have one instance of the function that is used by all instances, e.g.
function NewPerson(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
}
NewPerson.prototype.getName = function() {
alert("The age is "+this.age);
return this.name;
}
If you want to get some value from called function you must use
return value;
So try
function NewPerson(name, age, sex){
this.name = name;
this.age = age;
this.sex = sex;
this.getName = function(){
alert("The age is "+this.age);
return this.name;
};
}
var obj1 = new NewPerson("Mark",25,"Male");
alert("The name is as follows "+obj1.getName());
will work as you expected.
As stated before, you didn't return anything in getName.
It looks like you don't really want to alert age in getName. Also, adding getName to each instance of the object in the constructor is inefficient. Use the prototype method instead. I am submitting the cleaned-up code for what you are probably going for:
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
};
Person.prototype.getName = function() {
return this.name;
};
var obj1 = new Person("Mark",25,"Male");
alert("The name is: "+obj1.getName());
By placing getName on the prototype, all instances of Person will have access to the same function, thus saving memory :) By using the statement this.getName = ... in the constructor function Person, you are making a new function for each instance of Person.
#Davenewton 's answer is correct and while I like it for its
straightforwardness, it takes that
famous a-ha! moment for you to fully
realize why. Here's what's really going on:
So to elaborate
If we had something like this:
function i_am(){
var a = 'very smart person';
console.log(this.a);
}
var a = 'monkey';
i_am();
Depending on what linter or browser you might be using, this might either return
> "monkey" // (try the snippet)
or also
> "monkey"
> undefined // check the sreenshot
This is because of one simple reason, best understood if we go step-by-step how compiler "reads" the code.
hoist the function i_am() declaration to top (undefined)
hoist the var a declaration to top (undefined)
assign 'monkey' to variable a (now string "monkey")
execute/invoke the i_am() a function we have pre-declared
create a local variable a and assign string "very smart person"
console.log(this.a) returns 'monkey', because this refers to var/property a (same thing in this case) of the global object - call-site is from global object : i_am();
Finish execution - but uh oh! We executed function and returned no value!
Now, depending on your environment, either it hides the undefined result from you, or it prints it out! Why? Because functions are looking to return a value. Just like with variable declaration, if you do
var a;
console.log(a);
it has not been assigned a value, hence it results after RHS in undefined.
Similar principle applies to function, where it's looking to return a value, but it doesn't return anything, hence resulting in undefined
In other words, it's not this that returns undefined. It's the invokation of i_am(); that returns undefined!
Hopefully it's clear to everybody now :)
Related
This code is from sololearn.com in the JS course.
function person(name, age) {
this.name= name;
this.age = age;
this.yearOfBirth = bornYear;
}
function bornYear() {
return 2016 - this.age;
}
this.yearOfBirth = bornYear; why is bornYear just bornYear?
Shoudn't it be bornYear()
How does this even work?
I think this version would help you better to understand and is probably what the teacher wanted to write:
function Person(name, age) {
this.name= name;
this.age = age;
this.getYearOfBirth = function bornYear() {
return 2016 - this.age;
};
}
1- Always use Pascale case with a function that will be treated as class, that is why I renamed person to Person. That improves readability and confusions
2- I renamed yearOfBirth to getYearOfBirth, I agree the teacher should have given a better naming here too. Devs idiomatically use verbs to accessors (get/set) or methods (compute, parse, find...)
3- bornYear is a function within a class, so that an accessor is defined. Since bornYear is called without new operator, this will be equaled to window as default binding
Try it yourself, open your javascript console in the browser and type following:
function t() { console.log(this); }
t() returns window, the global/default scope
new t() returns a freshly created context t
bornYear is a function.
bornYear() is a value returned by burnYear function and equals 2016-this.age
What happens if this.yearOfBirth = bornYear
this.yearOfBirth = bornYear means you assign this.yearOfBirth=function(){return 2016-this.age}
Then lets say var me = new person("john",16) you can call me.yearOfBirth(); // 2000
What happens if this.yearOfBirth = bornYear(),
while you are declaring var me = new person("john",16) bornYear() was executed return 2016-this.age but this.age is not defined so returned undefined. Then, this.yearOfBirth=undefined.
function person(name, age) {
this.name= name;
this.age = age;
this.yearOfBirth = bornYear;
}
function bornYear() {
return 2016 - this.age;
}
var p = new person("A", 22);
document.write(p.yearOfBirth());
This code explains it.
A new object is created (new)
yearOfBirth is used because it returns a value
If you check the value, it's what it should be.
(Bad explaining)
The title is probably confusing but I will use a code snippet so hopefully you can explain what's going on.
My constructor
function Person(name, age){
this.name = name;
this.age = age;
this.yearsToRetire = function(){
console.log(this.age); //undefined
return 65-this.age;
}();
}
var joe = new Person ("joe",26);
console.log(joe.yearsToRetire); //NaN
My question is how come it doesn't know the value of this.age when I have already passed it and it should be 26 at the time of execution of yearsToRetire? Is it possible to achieve the effect of getting the years until retirement as the return value rather than a function to execute?(i.e is there a way that I use joe.yearsToRetire to get 39 rather than joe.yearsToRetire())
Thanks
this within the function refers to the global window object or undefined in strict mode, not the Person. One solution is to use an arrow function with lexical this:
function Person(name, age) {
this.name = name;
this.age = age;
this.yearsToRetire = (() => {
return 65 - this.age;
})();
}
var joe = new Person("joe", 26);
console.log(joe.yearsToRetire); // 39
Of course, the simpler solution here is to get rid of the immediately invoked function expression (IIFE) and write this.yearsToRetire = 65 - this.age;
Alternatively, add yearsToRetire as a function to the prototype:
Person.prototype.yearsToRetire = function() {
return 65 - this.age;
}
Or just remove the immediate invokation, as suggested by #ScottMargus - depending on what you want to achieve.
There are multiple problems. First you are invoking your function when you declare it which is putting the value of yearsToRetire as NaN. So remove the () at the end of your yearsToRetire function declaration. Second, you then need to call your function as a function: joe.yearsToRetire() instead of joe.yearsToRetire.
function Person(name, age){
this.name = name;
this.age = age;
this.yearsToRetire = function(){
return 65-this.age;
};
}
var joe = new Person ("joe",26);
console.log(joe.yearsToRetire());
You can call the function in the Person's scope using call or apply or simply
save a reference to the person object:
function Person(name, age){
var self = this;
this.name = name;
this.age = age;
this.yearsToRetire = (function(){
console.log(self.age);
return 65-self.age;
})();
}
var joe = new Person ("joe",26);
console.log(joe.yearsToRetire);
You have three issues:
You were self-invoking the function
You weren't calling for the yearsToRetire function to run, you were just referencing it.
Methods generally are added to an object's prototype so that each new instance doesn't have to store the same functionality over and over again.
Now, as for #1 (above), function declarations are hoisted to the top of their enclosing scope and are read into memory at the start of that scope's processing. When the function is read, if it includes other function invocations, those functions will be invoked and that return values of those functions will be used as the values for the function being read. That was your main problem. You thought that the self-invocation of yearsToRetire wouldn't happen until a new instance of Person was made, but in fact, that invocation was happening immediately.
This is not the pattern to use when creating constructor functions.
function Person(name, age){
this.name = name;
this.age = age;
}
// By adding the function to the prototype, each Person instance
// will inherit the method.
Person.prototype.yearsToRetire = function(){
console.log("The passed age was: " + this.age);
return 65-this.age;
}; // <-- You were self-invoking the function here
var joe = new Person ("joe",26);
console.log(joe.yearsToRetire()); // <-- You didn't include () after function name here
I am reading John Resig's slideshow http://ejohn.org/apps/learn/#78
Its not clear to me why i need to include .prototype in the line Me.prototype = new Person();
function Person(){}
Person.prototype.getName = function(){
return this.name;
};
function Me(){
this.name = "John Resig";
}
Me.prototype = new Person();
var me = new Me();
assert( me.getName(), "A name was set." );
Think of it this way, if every person is going to have a name, it's easier to assign the getName function to it's prototype.
In this case, you have a Person that has a prototype function to get a name, and a few lines below you have a Me function that assigns a name by default. Since we want to incorporate the getName function, we use the Person which already has that function object built-in. When you construct Me() and assign to me you can call getName() and use that to return the name, which by default is "John Resig"
The same thing can be achieved without prototype, like this
function Me(){
this.name = "John Resig";
this.getName = function () {
return this.name;
}
}
var me = new Me();
...BUT by using prototype, it helps create objects a lot faster, you can refer to this answer as well.
I am trying to figure out how I can add methods to a constructor after I have created it.
In my code below, I cannot use Person's prototype property to add a new public method which has access to Person's vars. (Do the functions attached to the prototype property not close over the vars in the main function).
Unlike the first way, the second way works - Person 2. seems like these are called privileged methods -http://www.crockford.com/javascript/private.html.
function Person(name, age){}
Person.prototype.details = function(){
return "name: "+name+", age: "+age;
};
function Person2(name, age){
this.details = function(){
return "name: "+name+", age: "+age;};
}
var per1 = new Person("jim", 22);
var per2 = new Person2("jack", 28);
per1.details();
//=> ReferenceError: age is not defined
per2.details();
//=> "name: jack, age: 28"
No, they do not have closure over the constructor functions vars. They are in a different scope.
// This function is in one scope.
function Person(name, age) {
}
// This statement is in the parent scope, which
// doesn't have access to child scopes.
Person.prototype.details = function(){
return "name: "+name+", age: "+age;
};
That's the way that "public" functions work in JavaScript. You could make details a privileged function by defining it within the constructor:
function Person(name, age) {
this.details = function() {
return "name: "+name+", age: "+age;
};
}
Of course, that means that each instance of Person gets it's own copy of the details function.
You could also, as #Chuck suggests, make name and age public members, in which you would have access to them in a prototype function:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.details = function(){
return "name: " + this.name + ", age: " + this.age;
};
No. Typically you would either use the second approach, or set this._name = name; in the constructor and reference it that way in the other method.
Of course not, the function was declared in a scope, different to the scope where the arguments/variables were declared, so JS wouldn't know which variables you're on about. Suppose you had a second closure, or better (well, worse actually) yet: a global variable called name. Which one would JS pick?
Here's an example for you:
function MyObject(name)
{
var localVar = 'foobar';
this.evilMethod = (function(localVar)
{
return function()
{
console.log('localVar = '+localVar);//=== name
};
})(name);
this.badMethod = function()
{
console.log('localVar = '+ localVar);// === 'foobar'
};
}
var name = 'Global Name';
var anotherClosure = (function(name)
{
var localVar = name.toLowerCase();
return function()
{
console.log(name);
console.log(localVar);
}
})('Bobby');
MyObject.prototype.closureVars = function()
{
console.log(name);//Global name
console.log(localVar);//undefined
};
Now first off: this is terrible code, but you get the point: you can have hundreds of variables with the same name, which one JS has to use, might not always be clear.
Giving prototypes access to instance closure variables has other implications, too: you could, for instance change their values, which defeats the point of having a closure in the first place. But the biggest problem by a country mile would be: multiple instances! If you create a constructor, odds are you're going to instantiate more than 1 object with it. How would that work, if they all share the same prototype?
Just assign the arguments/variables you want to access in the prototype to a property, like FishBasketGordo's example does
<html>
<head>
<script type="text/javascript">
function Person (name, age) {
this.name = name;
this.age = age;
this.sayName = function () {
alert(this.name);
}
}
var person1 = new Person ("tom", 29);
var person2 = new Person ("frank", 21);
alert(person1.sayName==person2.sayName);
</script>
</head>
<body>
</body>
</html>
There is nothing wrong with it (other than the slightly pedantic missing semicolon on line 6.)
Because the sayName function is created inside the constructor, a new function is created every time a new object is created. (So the functions are different, and == returns false)
People get around this by attaching the function to the prototype object instead:
function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
alert(this.name);
};
var person1 = new Person ("tom", 29);
var person2 = new Person ("frank", 21);
alert(person1.sayName==person2.sayName);
This will create only one function (saving you memory) and the alert will say 'true'.
You are comparing the function pinters, not the results.
Try:
alert( person1.sayName() == person2.sayName() );
But then again: your sayName() triggers another alert(). What is this code all about??
person1 and person2 are different objects, so their comparison should be false.
However, you may have meant to compare the functions literally, which you can with toString(), in which case, the alert is true.
jsFiddle.
And of course, they both have a different this.name so if they did return that, and you called the functions and compared them, it would be false as well.