Problem with anonymous function and undefined members values - javascript

I asked a question here:
Extending javascript literal object
which was solved because I forgot return. Now I didn't forget return and I got undefined again, why ?
<script>
var secretAgent = (function(){
var person = {},
firstName = "James",
lastName = "Bond";
person.WhoAreYou = function() {
alert("My name is " + this.lastName + ", " + this.firstName + " " + this.lastName);
};
return person;
})();
</script>
<script>
secretAgent.WhoAreYou();
</script>
Update: why mine doesn't work whereas I think I did the same thing as the one below that works:
http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/
//Revealing Module Pattern (Public & Private)
var skillet = (function() {
var pub = {},
//Private property
amountOfGrease = "1 Cup";
//Public property
pub.ingredient = "Bacon Strips";
//Public method
pub.fry = function() {
console.log( "Frying " + pub.ingredient );
};
//Private method
function privateWay() {
//Do something...
}
//Return just the public parts
return pub;
}());
//Public Properties
console.log( skillet.ingredient ); //Bacon Strips
//Public Methods
skillet.fry();
//Adding a public property to a Module
skillet.quantity = 12;
console.log( skillet.quantity ); //12
//Adding a public method to a Module
skillet.toString = function() {
console.log( skillet.quantity + " " +
skillet.ingredient + " & " +
amountOfGrease + " of Grease" );
};
try {
//Would have been successful,
//but can't access private variable
skillet.toString();
} catch( e ) {
console.log( e.message ); //amountOfGrease is not defined
}

You need to declare those properties on the literal itself (rather than separate unrelated variables), like this:
var secretAgent = (function(){
var person = { firstName: "James", lastName: "Bond" };
person.WhoAreYou = function() {
alert("My name is " + this.lastName + ", " + this.firstName + " " + this.lastName);
};
return person;
})();
You can test it out here.

There are two problems here, as I see it.
You did forget the return, again. :-) The WhoAreYou function doesn't actually return anything, it just alerts. Hence secretAgent.WhoAreYou() returns undefined too.
The alert shows "My name is undefined, undefined undefined". This is because of the scope of the variables used. You assign the WhoAreYou to person, and within the body you reference this.lastName. The this here refers to the person variable, and as you can see this object does not have a lastName property.
There are two ways then that you can fix the latter issue. Firstly, by adding the name variables to the person object:
var secretAgent = (function(){
var person = {};
person.firstName = "James";
person.lastName = "Bond";
person.WhoAreYou = function() {
alert("My name is " + this.lastName + ", " + this.firstName + " " + this.lastName);
};
return person;
}
)();
// Although the first three lines would be more natural as:
var person = { firstname: "James", lastName: "Bond" };
Secondly, you can instead choose to drop the this reference, which will instead refer to the local variables you just defined:
var secretAgent = (function(){
var person = {},
firstName = "James",
lastName = "Bond";
person.WhoAreYou = function() {
alert("My name is " + lastName + ", " + firstName + " " + lastName);
};
return person;
}
)();
You'll of course need to add appropriate returns to the WhoAreYou function in both examples.

Remove "this" from the variables since you are setting them as a var with in the anonymous function, using this points to that function and not "Person" which is where you are now calling them.
<script>
var secretAgent = (function(){
var person = {},
firstName = "James",
lastName = "Bond";
person.WhoAreYou = function() {
alert("My name is " + lastName + ", " + firstName + " " + lastName);
};
return person;
}
)();
</script>
<script>
secretAgent.WhoAreYou();
</script>
example here: JSFiddle

Why not just:
var secretAgent = {
firstName: "James",
lastName: "Bond",
whoAreYou: function() {
alert("My name is " + this.lastName + ", " +
this.firstName + " " + this.lastName);
}
};

The way you defined firstName and lastName are not fields to object person itself. But they are upper values to function WhoAreYou. So you could write the function like this:
person.WhoAreYou = function() {
alert("My name is " + this.lastName + ", " + this.firstName + " " + this.lastName);
};
Its like if those were private variables to the function. The alternative is declaring as filelds to the object itself like this:
var person = {
firstName: "James",
lastName: "Bond",
};
The the method whould work as you wrote it.

The variable this is person in your case, also firstName and lastName are defined as local variables, but not as properties of the person, so you can just access them within the anonymous function by names:
var secretAgent = (function(){
var person = {},
firstName = "James",
lastName = "Bond";
person.WhoAreYou = function() {
alert("My name is " + lastName + ", " + firstName + " " +lastName);
};
return person;
})();
secretAgent.WhoAreYou();

Related

Creating constructors using "this" instead of simply returning an object

My knowledge of words might not be sufficient to find this by myself the explanation on the www. So please excuse me if this might be a duplicate.
I'm currently trying to figure out why we use "this" in a function constructor instead of simply returning an object?
E.g. this JSFiddle
// Using this inside function
function Student1(first,last) {
this.firstName = first;
this.lastName = last;
this.display = function(){
return this.firstName + " " + this.lastName;
};
}
const harry = new Student1("Harry", "Potter");
document.querySelector("div").innerHTML = harry.display();
document.querySelector("div").innerHTML += "<br>";
// Returning object
function Studen2(first,last){
return {
firstName: first,
lastName: last,
display(){
return this.firstName + " " + this.lastName;
}
};
}
const ron = new Student1("Ron", "Weasley");
document.querySelector("div").innerHTML += ron.display();
Anyone mind to explain or guide me to the right direction?
this works with the prototype of the instanciable function, whereas the simple object has another prototype in the chain of prototypes. It has not an own prototype of the instanciable function.
You could add a new method to the prototype and watch the difference.
// Using this inside function
function Student1(first,last) {
this.firstName = first;
this.lastName = last;
this.display = function(){
return this.firstName + " " + this.lastName;
};
}
const harry = new Student1("Harry", "Potter");
Student1.prototype.morning = function () { return 'good morning ' + this.firstName + " " + this.lastName; };
console.log(harry.morning());
// Returning object
function Studen2(first,last){
return {
firstName: first,
lastName: last,
display(){
return this.firstName + " " + this.lastName;
}
};
}
const ron = new Student1("Ron", "Weasley");
Student2.prototype.morning = function () { return 'good morning ' + this.firstName + " " + this.lastName; };
console.log(ron.morning());
Generally you will want to define the object methods on the class prototype, so that they don't have to be reinstantiated every time you create a new instance of the class, e.g.:
function Student1(first,last) {
this.firstName = first;
this.lastName = last;
}
Student1.prototype.display = function() {
return this.firstName + " " + this.lastName;
}
const harry = new Student1("Harry", "Potter");
document.querySelector("div").innerHTML = harry.display();
If you just return an (anonymous) object, it won't have a prototype and you'll have to define the function every time the construction function gets called.
Also, in your example:
harry instanceof Student1 // true
ron instanceof Student2 // false
So you can't use instanceof.

How to get the attribute of a JS object from Java?

I know I can use the Invocable class to invoke methods on a class:
import javax.script.{ScriptEngine, ScriptEngineManager, Invocable}
val engine = new ScriptEngineManager().getEngineByExtension("js")
val invoker = engine.asInstanceOf[Invocable]
val person = engine.eval(s"""
new function () {
this.name = "Rick";
this.age = 28;
this.speak = function () {
return this.name + "-" + this.age;
}
};
""")
invoker.invokeMethod(person, "speak") //returns "Rick-28"
But, how do I get the name attribute of the person? I tried invoker.invokeMethod(person, "name") and I got a NoSuchMethodError.
You can cast person to a JSObject and then call person.getMember("name"). Full Java example:
ScriptEngine engine = new ScriptEngineManager()
.getEngineByExtension("js");
JSObject rick = (JSObject) engine.eval("new function () {\n" +
" this.name = \"Rick\";\n" +
" this.age = 28;\n" +
" this.speak = function () {\n" +
" return this.name + \"-\" + this.age;\n" +
" }\n" +
" };");
System.out.println(rick.getMember("name"));
Or, if the object is stored in the engine global scope like in the following javascript source:
rick = function() {
this.name= "Rick";
};
you can then call
engine.eval("rick.name");

Creating functions in function prototypes for Javascript

How do I properly create a function within a function prototype?
What I have is this:
<body>
<p id="demo"></p><script>
function person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
person.prototype.name = function() {
return {
myFunc: function() {
this.firstName + " " + this.lastName;
}
}
};
var myFather = new person("John", "Doe", 50, "blue");
document.getElementById("demo").innerHTML =
"My father is " + myFather.name().myFunc;
</script>
</body>
When I run this it returns "My father is function () { this.firstName + " " + this.lastName; }" , but I was expecting John Doe.
You need call function, add () to myFunc. In your example you added reference to internal function.
document.getElementById("demo").innerHTML = "My father is " + myFather.name().myFunc();
Also add return to myFunc. To get properties from parent scope - save reference to this
person.prototype.name = function () {
var _this = this;
return {
myFunc: function () {
return _this.firstName + " " + _this.lastName;
}
}
};
Example
Myfunc is a function. When you call it, call like myfunc()
You are not calling myFunc and also that function does not return anything. I find this cleaner and a better way to define the funciton prototype:
function Person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
Person.prototype = {
name: function() {
return this.firstName + " " + this.lastName;
}
};
Note that name now returns return this.firstName + " " + this.lastName;.
Then simply:
document.getElementById("demo").innerHTML = "My father is " + myFather.name();

Javascript prototype handling self and constructor parameters

Im trying to understand javascript prototyping principles and cant get some basic things to work.
One thing that im trying to achieve is, to create a base object, that handles constructor input, and sets values based on that input, or default values if no constructor parameter is given.
Also, i cant quite figure out how to store this this in a variable so that it would point to correct instance of the object( parent object perhaps )
Below are 2 versions which i have tried to create basic inheritance. ( first one ive seen used before, but it doesnt allow me to handle constructor parameters passed to extended object using its base object's constructor.
The second version...is something i came up with to enable it, but...i have never seen anyone using prototypes like this and im sure its the wrong way ( since the prototype property is a function instead of an object)
Whats the correct way of solving both of the problems.
var Person = function(conf) {
if (!conf) {conf = {};}
var _person = this;
this.first_name = conf.first_name;
this.last_name = conf.last_name;
this.greet = function() {
alert("Hi, im " + this.first_name + " " + this.last_name );
}
this.callback_greet = function() {
alert("Hi, im " + _person.first_name + " " + _person.last_name );
console.log("this:", this, " _person:", _person );
}
}
var Student = function(conf) {
if (!conf) {conf = {};}
/* id like to pass this conf on to Person constructor */
this.report = function() {
alert( this.first_name + " " + this.last_name + " is ready to study" );
}
}
Student.prototype = new Person();
var Teacher = function(conf) {
if (!conf) {conf = {};}
this.teach = function() {
alert( this.first_name + " " + this.last_name + " is ready to teach...maggots" );
}
}
Teacher.prototype = new Person();
student = new Student({first_name: "Mike", last_name: "Stud"});
//student.first_name="Mike";
//student.last_name="Stud";
student.greet();
/* alerts Hi, im Mike Stud */
teacher = new Teacher();
teacher.first_name="John";
teacher.last_name="Smith";
teacher.teach();
/* alerts John Smith is ready to teach...maggots */
teacher.callback_greet ();
/* both alerted values are undefined */
//_________________________
//_______Version 2 _______
//_________________________
var Person = function(conf) {
if (!conf) {conf = {};}
var _person = this;
this.first_name = conf.first_name;
this.last_name = conf.last_name;
this.greet = function() {
alert("Hi, im " + this.first_name + " " + this.last_name );
}
this.callback_greet = function() {
alert("Hi, im " + _person.first_name + " " + _person.last_name );
console.log("this:", this, " _person:", _person );
}
}
var Student = function(conf) {
if (!conf) {conf = {};}
this.prototype = Person;
this.prototype(conf);
this.report = function() {
alert( this.first_name + " " + this.last_name + " is ready to study" );
}
}
var Teacher = function(conf) {
if (!conf) {conf = {};}
this.prototype = Person;
this.prototype(conf);
this.teach = function() {
alert( this.first_name + " " + this.last_name + " is ready to teach...maggots" );
}
}
var Principal = function(conf) {
if (!conf) {conf = {};}
this.prototype = Teacher;
this.prototype(conf);
this.dicipline_teachers = function() {
alert( this.first_name + " " + this.last_name + " thy all mighty principal is watching you" );
}
}
student = new Student({first_name: "Mike", last_name: "Stud"});
student.greet();
/* alerts Hi, im Mike Stud */
teacher = new Teacher({first_name: "John", last_name: "Smith"});
teacher.teach();
/* alerts John Smith is ready to teach...maggots */
principal = new Principal({first_name: "David", last_name: "Faustino"});
principal.teach();/* alerts David Faustino is ready to teach...maggots */
principal.dicipline_teachers();/* David Faustino thy all mighty principal is watching you*/
Well, your second version is actually… somewhat… correct!
What your snippet
var Student = function(conf) {
this.prototype = Person;
this.prototype(conf);
does here:
Have a Student instance as this when being invoked with new Student()
Create a property on the instance that contains a function (in this case, the parent constructor), which essentially creates a method on the instance
Call that as a method. Which means, the Person function is invoked with its this pointing to the instance that we have here - and then Person does its setup on that instance.
This is just what we want. Maybe except for creating the unnessary property. Notice that the name of this property being prototype is totally irrelevant for its function, you could have use myParentConstructor or so as well.
In the standard JavaScript inheritance, we do a similar thing to that method call - we want to call the parent constructor on the current (child) instance so that it does get set up. However, we use the .call() method for that.
Now we also want to use the prototype. In your code, all the methods greet, report, teach and dicipline_teachers could be shared amongst the instances, so they can - and should - go on the ConstructorFn.prototype. To let all teachers, students, and principals inherit these methods we need to set up a prototype chain (inheritance hierarchy). We don't want to use new Person as that would call the constructor and set up things like first_name on the prototype object where they would to be shared - but we don't want that. Instead, we use Object.create.
Alltogether, your code would look like this:
function Person(conf) {
if (!conf) {conf = {};}
var _person = this;
this.first_name = conf.first_name;
this.last_name = conf.last_name;
this.callback_greet = function() {
alert("Hi, im " + _person.first_name + " " + _person.last_name );
console.log("this:", this, " _person:", _person );
};
}
Person.prototype.greet = function() {
alert("Hi, im " + this.first_name + " " + this.last_name );
};
function Student(conf) {
Person.call(this, conf);
}
Student.prototype = Object.create(Person.prototype, {constructor:{value:Student}});
Student.prototype.report = function() {
alert( this.first_name + " " + this.last_name + " is ready to study" );
};
function Teacher(conf) {
Person.call(this, conf);
}
Teacher.prototype = Object.create(Person.prototype, {constructor:{value:Teacher}});
Teacher.prototype.teach = function() {
alert( this.first_name + " " + this.last_name + " is ready to teach...maggots" );
};
function Principal(conf) {
Teacher.call(this, conf);
}
Principal.prototype = Object.create(Teacher.prototype, {constructor:{value:Principal}});
Principal.prototype.dicipline_teachers = function() {
alert( this.first_name + " " + this.last_name + " thy all mighty principal is watching you" );
};
(1) It's best to handle setting default values after a proper sanity check on the type(s) of value(s) passed:
var Constr = function (conf) {
if (!!conf && !(conf instanceof Object)) {
throw new Error('An invalid parameter was passed to Constr.');
}
if (!conf) { // Prevent "Can't read property 'name' of undefined."
conf = {};
}
this.name = conf.name || null; // Set defaults this way.
};
(2) You'll want to use Object.create() and Object.apply() to get this right:
var Person = function (param1, param2) {
this.param1 = param1;
this.param2 = param2;
}
Person.prototype.cough = function () {
// Do stuff.
}
var Student = function (param1, param2, paramN) {
Person.call(this, param1, param2);
this.paramN = paramN; // Define a new property on the subclass
}
// Invoke the superclass to have the subclass inherit properties and methods.
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

How to call a method in class from its replaced implementation?

Hai,
I am trying to understand few concepts in JavaScript. Consider the following code:
function Person(name, age)
{
this.name = name || "no name";
this.age = age || "age not specified";
this.printStr = function()
{
console.log("< " + this.name + ", " + this.age + " >");
};
}
p = new Person("pranav", 26);
p.printStr = function()
{
console.log("this works. also ...." + this.name);
};
p.printStr();
I want to call the implementation of 'printStr' in Person class from within the implementation of 'printStr' function in 'p'.
such that the output should be:
< pranav, 26 >
this works. also ....pranav
Any ideas? :)
The way your code is set up now, you can't do it. When you call Person as a constructor, the object that ends up being p gets set to this. So when you define printStr in the constructor, p gets an attribute called printStr. You then over-write it when you assign the second function.
Two options: A non-answer is to do what pablochan did - have the internal one be called oldPrintStr. Another option is to use the prototype inheritance:
function Person(name, age)
{
this.name = name || "no name";
this.age = age || "age not specified";
}
Person.prototype.printStr = function() {
console.log("< " + this.name + ", " + this.age + " >");
};
Then you can do this:
p = new Person("pranav", 26);
p.printStr = function()
{
Person.prototype.printStr.apply(this);
console.log("this works. also ...." + this.name);
};
p.printStr();
As far as I know there is no real subclassing in JS so to do this you should probably save the old function and then replace it.
p = new Person("pranav", 26);
p.oldPrintStr = p.printStr;
p.printStr = function()
{
p.oldPrintStr();
console.log("this works. also ...." + this.name);
};
p.printStr();
unless you save Person's printStr you can always create a temp Person object solely to extract printStr and call it:
p.printStr = function()
{
print("this works. also ...." + this.name);
(new Person()).printStr.apply(this);
};
but I guess you'll be better off if you make Person's original printStr accessible via prototype:
Person.prototype.printStr = function()
{
print("< " + this.name + ", " + this.age + " >");
};
then you have no need for temp object or saving old function and can do:
Person.prototype.printStr.apply(this);

Categories

Resources