One question about JS constructor function:
var hm = {};
new function(name){
hm[name] = this;
}("hello")
Could anyone give me a little explanation about how this constructor running (such as which part runs first)
First var hm becomes an Object, then a new Anonymous function is called with ("hello") passed into name argument. The important thing to understand here is that when you see () with or without any arguments after a function name or an Anonymous function, it calls the function. In this case the new keyword and the fact that there is a this property inside the function makes it a Constructor. hm inside the Constructor creates a property based on the name argument and assigns the new instance itself to hm[name], as this refers to each new instance. End result is that hm.hello or hm['hello'] now refer to the new instance. Of course, all code runs from top to bottom and according to standard order of operations, like String resolution before assignment. Also, note that, this won't work:
func('wow');
var func = function(x){
console.log(x);
}
This will work:
func('wow');
function func(x){
console.log(x);
}
If you lack understanding of Constructors altogether you should know that a Constructor is used so you can have multiple instances of similar Objects. For instance:
function Person(last, first, middle){
this.lastName = last; this.firstName = first; this.middleName = middle;
this.said = this.ate = '';
this.saySomething = function(anything){
this.said = anything;
return this;
}
this.eatSomething = function(food){
this.ate = food;
return this;
}
this.didWhat = function(){
var m = this.middleName ? ' '+this.middleName : '';
var n = this.firstName+m+' '+this.lastName;
if(this.said){
n += ' said, "'+this.said+'"';
n += this.ate ? ', and' : '.';
}
if(this.ate){
n += ' ate '+this.ate+'.';
}
return n;
}
}
var Bob = new Person('Small', 'Bob', 'Richard');
Bob.saySomething('This programming stuff is pretty cool.').eatSomething('Some Vegan Food');
console.log(Bob.didWhat());
var Sally = new Person('Jones', 'Sally');
Sally.saySomething("It just takes time, but you'll get it.");
console.log(Sally.didWhat());
Remember that the keyword this refers to the instance itself. In the example above I have created Bob and Sally Objects by calling new instances of Person. By returning this within a Constructor method you can chain methods, since the result of the executing method with be the instance itself.
Note that
Bob.saySomething('This programming stuff is pretty cool.').eatSomething('Some Vegan Food');
is the same as
Bob.saySomething('This programming stuff is pretty cool.');
Bob.eatSomething('Some Vegan Food');
since, as far as .eatSomething() is concerned, Bob and this are synonymous.
If you just want to access a property, it's like:
console.log(Bob.said);
console.log(Bob.lastName);
Bob.said = "Now you're getting it.";
console.log(Bob.didWhat());
Related
I am learning objects in JavaScript and I don't understand methods assigned as property to objects, when objects are defined through user-defined functions.
This is a code snippet from tutorial point.com/JavaScript
<script type = "text/javascript">
function addPrice(amount) {
with(this){
price = amount;
}
}
function book(title, author) {
this.title = title;
this.author = author;
this.price = 0;
this.addPrice = addPrice;
}
</script>
<script type = "text/javascript">
var myBook = new book("Perl", "Mohtashim");
myBook.addPrice(100);
document.write("Book title is : " + myBook.title + "<br>");
document.write("Book author is : " + myBook.author + "<br>");
document.write("Book price is : " + myBook.price + "<br>");
</script>
I want to know how the this.addPrice = addPrice is working and why if I remove the line no output is shown?
JS is pretty flexible language. In JS this:
var obj = {
name: "Raju Ritigya",
sayHi: function() {
console.log("hello, I'm " + this.name);
}
};
Is the same as this:
var obj = {};
obj.name = "Raju Ritigya";
obj.sayHi = function() {
console.log("hello, I'm " + this.name);
};
Basically, there are two ways of adding properties and methods to an object in JS.
With that being said, your course is teaching you how to write "Classes", constructors and this in JS. IMO if you're just starting with JS, this is too complex to understand. JS doesn't support Classes natively and it tries to mimic them with prototype inheritance.
But anyway, here's my 0.02$ on what's going on there
In JS you have primitive types (string, number, boolean, symbol, null, undefined) and everything else is an object (yes, array is an object, function is an object, object is an object).
Primitive values are passed around by value and they are immutable, but objects are passed by reference (a point in memory) and they are mutable
var foo = {};
var bar = {};
console.log(foo === bar) //false
Even though foo and bar look the same, they point to different places in memory, hence for JS they are not the same!
var foo = {};
var bar = foo;
bar.name = "random name";
console.log(foo.name); // "random name"
And now foo and bar point to the same reference and making changes to one of them reflect to the other one.
In JS every function needs to return something. If you don't explicitly put a return statement in your function it will return undefined and if you use new keyword in front of your function call, it will return a new object that will have that function as a constructor.
So, in conclusion, what's going on there is that you have a constructor (book) that's gonna return an object with 3 properties (author, title, price) and a method (addPrice). That method is a function (and as we already said, functions are objects in JS and can be easily passed around). It would be exactly the same if you wrote your constructor like this:
function book(title, author) {
this.title = title;
this.author = author;
this.price = 0;
this.addPrice = function(amount) {
this.price = amount
};
}
As #deceze mentioned, using with is highly discouraged.
By removing the this.addPrice = addPrice line, you don't add a method to your object but later on you try to call it on this line myBook.addPrice(100);
Your code breaks on that line and JS won't continue executing the rest of your program (you can open console tab in dev tools and see the error there).
Hope it helps,
Cheers!
I've been reading the chapter on functional inheritance in Crockford's 'The Good Parts'. In the mammal example he gives I'm a bit confused as to why he uses the superior method to modify the get_name function. Here is the example in question:
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};
var mammal = function (spec) {
var that = {};
that.get_name = function () {
return spec.name;
};
that.says = function () {
return spec.saying || '';
};
return that;
};
var myMammal = mammal({
name: 'Herb'
});
var cat = function (spec) {
spec.saying = spec.saying || 'meow';
var that = mammal(spec);
that.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
that.get_name = function () {
return that.says() + ' ' + spec.name + ' ' + that.says();
};
return that;
};
Object.method('superior', function (name) {
var that = this,
method = that[name];
return function () {
return method.apply(that, arguments);
};
});
var coolcat = function (spec) {
var that = cat(spec);
var super_get_name = that.superior('get_name');
that.get_name = function (n) {
return 'like ' + super_get_name() + ' baby';
};
return that;
};
var myCoolCat = coolcat({
name: 'Bix'
});
var name = myCoolCat.get_name(); // 'like meow Bix meow baby'
I'm confused about this because I can replicate the same thing by removing the superior method and just changing coolcat as follows:
var coolcat = function(spec) {
var that = cat(spec);
var super_get_name = that.get_name();
that.get_name = function(n) {
return 'like ' + super_get_name + ' baby';
};
return that;
};
So, I don't understand why Crockford chooses to use the superior method. Is anyone able to explain at all?
The idea here is that this:
var super_get_name = that.superior('get_name');
makes super_get_name into a function that — every time it is called — invokes that's original get_name method. This allows the new get_name to call the old (super-class) get_name.
Now, if the original get_name method will never have any effect other than to return a single value that never changes, then yeah, this is kind of pointless; you can just save that single-value-that-never-changes and then use it in the new get_name. But if the original get_name can actually do things (such as, say, run an AJAX request, or change the styling of an HTML element), or if its return-value can change (say, if there were some corresponding set_name method), then there would be an important difference between what your code does (save the original return-value and use it) and what Crockford's code does (save the original method and invoke it).
The confusion that arises from this chapter of Crockford's book arises as what Crockford describes is "his" preferred pattern for implementing inheritance in JavaScript, which relies on his extending the Function object with the Function.prototype.method (chapter 1.3) which he uses to add methods to the Function object.
The problem addressed in the coolcat example is the need to access the method of the parent type. In 'classical' OO languages like Java this is natural as classes exist in their own right. In JavaScript inheritance is prototypical, you make an object of type mammal and then modify the object to create the type cat or coolcat.
Depending on your design you may add properties and functions or override an 'inherited' function. The problem arises when you override an 'inherited' function, in JavaScript you basically replace the old function with the new function, thereby loosing the older function.
Crockford now needs to do two things:
get the parent's (cat's) get_name method; and
save it in a manner that can be used from within the overridden method.
In this code:
var coolcat = function(spec) {
var that = cat(spec),
super_get_name = that.superior('get_name');
that.get_name = function(n) {
return 'like ' + super_get_name() + ' baby';
};
return that;
};
He does 1. by calling the superior method to get a function that gets the cat's get_name function;
and he does 2. by saving it to the super_get_name variable within the coolcat function(/object) allowing access to the cat's get_name function before it is overridden (more correctly overwritten) by the coolcat's get_name function.
In my opinion the confusion arises because:
The superior method is named oddly: the 'superior' method is simply a function look up by name method and could be better named, for example as getFunctionByName (you can try this by replacing the string get_name, by purr, the coolcat's get_name will now call purr, just remember to call it as super_get_name(10) otherwise you'll get an empty string back).
Secondly the code and the pattern obfuscates the code by relying on some particular Crockford patterns, and is likely to stresses you out if you attempt to dive into this chapter without having followed the entire book.
I think there is a simpler way to achieve this, one that I think because it is completely localized is easier to understand etc., as in the code below:
var coolcat = function(spec) {
var that = cat(spec);
that.parent_get_name = that.get_name;
that.get_name = function() {
return 'like ' + this.parent_get_name() + ' baby';
};
return that;
};
There are some other oddities for example the argument n in definition of the coolcat get_name function, which I can only assume came from copying the purr function, which would suggest a ghost writer!
Finally, I would suggest that before one reads the book one should listen to his talk on 'JavaScript the good parts'. The talk is absolutely brilliant, much better than the book.
Let's say we inherit 90% of code functionality from a boilerplate prototype-based funciton:
var BoilerplateForOtherFunctions = function(){};
BoilerplateForOtherFunctions.prototype.addOneYear = function(){
return this.myAge += 1;
};
BoilerplateForOtherFunctions.prototype.getAge = function(myUrl){
return this.myAge;
};
And we have many functions that inherits this prototype and used it like this:
var MyNormalFnOne = function(){
this.myAge = "34";
console.log( this.getAge() );
this.addOneYear();
};
// only injects the boilerplate object into our function
underscore.extend(MyNormalFnOne.prototype, BoilerplateForOtherFunctions.prototype);
new MyNormalFnOne();
Everything works well.
Problem is - that we rely on a fact, that someone has defined this.myAge before we use anything from the boilerplate prototype. This is very common solution of programming in Javascript.
Wouldn't be better to pass all arguments into the prototype fn and be more functional? It would lead to faster debugging, allows function cashing, creates less coding mistakes and program errors.
Into something like this (more functional approach):
var BoilerplateForOtherFunctions = function(){};
BoilerplateForOtherFunctions.prototype.addOneYear = function(age){
return age += 1;
};
BoilerplateForOtherFunctions.prototype.getAge = function(age){
return age; // makes no sense, here - just a sample, of course
};
var MyNormalFnOne = function(){
var myAge = "34";
console.log( this.getAge(myAge) );
myAge = this.addOneYear(myAge);
};
// only injects the boilerplate object into our function
underscore.extend(MyNormalFnOne.prototype, BoilerplateForOtherFunctions.prototype);
new MyNormalFnOne();
The problem with second appoarch is (with a functional one)
- passing many arguments to every function
- return value must be assigned again, it doesn't "magically" work
The problem with first appoarch is (with a traditionals)
- difficult debugging, not reliable code, no fn cashing, ...
Any opinion?
The constructor function is there to initialize instance specific members and the prototype is for shared members (like functionality).
Since your prototype assumes myAge to exist you should set it in BoilerplateForOtherFunctions and re use it in types that inherit from it:
var BoilerplateForOtherFunctions = function(args){
args=args||{};
this.myAge = args.myAge === 0? 0 ://either 0
args.myAge || 10;//or 10 when not set
};
BoilerplateForOtherFunctions.prototype.addOneYear = function(){
return this.myAge += 1;
};
BoilerplateForOtherFunctions.prototype.getAge = function(myUrl){
return this.myAge;
};
var MyNormalFnOne = function(args){
//re using parent constructor
BoilerplateForOtherFunctions.call(this,args);
console.log( this.getAge() );
this.addOneYear();
};
// use Object.create will have instances of MyNormalFnOne be an instanceof BoilerplateForOtherFunctions
MyNormalFnOne.prototype = Object.create(BoilerplateForOtherFunctions.prototype);
//repair prototype.constructor after assigning a new value to prototype
MyNormalFnOne.prototype.constructor = MyNormalFnOne;
var oneFirst = new MyNormalFnOne();
var oneSecond = new MyNormalFnOne({myAge:22});
More info on constructor functions and prototype here: Prototypical inheritance - writing up
I have written a constuctor
function Human() {
var legs = 2;
var iq = 100;
}
Then I create an instance of the object
var Man = new Human();
And want to add a new method
Man.getIQ = function() {
return iq - 10;
}
But I'm told that IQ is undefined. Even if I use this.iq.
Why isn't a var inside the scope of an object available for new methods?
The variables legs and iq are simulating a "private" member to the Human class because they are visible only in that closure (only in the Human function).
If you want to access them from outside that scope, you need to make them public by binding them to the this keyword (this.iq=100;) or by implementing getters and setters for each of your private member :
function Human() {
var legs = 2;
this.getLegs = function(){
return legs;
};
}
Anyway, these are represent just the very tip of the iceberg; I explained them so that you can understand why what you were trying to do failed.
If I understand correctly what you were trying to do, the right way to write your ideea oop in js would be something like this :
function Human(){}
Human.prototype = {
legs : 2,
iq : 100
};
function Woman(){}
Woman.prototype = new Human;
Woman.prototype.getIq = function(){
return this.eq - 10;
};
var womanInstance = new Woman();
// needless to say that this line is both wrong and misogynistic
alert('a woman has an iq of ' + womanInstance.getIq());
Sorry if I got carried away, but there are lots of articles & blogs about javascript oop (such as this one) that I recommend you read before/while getting stuck with these kind of problems.
Just for the kicks i am trying to create a simple data object in javascript. Here is the code.
var roverObject = function(){
var newRover = {};
var name;
var xCord;
var ycord;
var direction;
newRover.setName = function(newName) {
name = newName;
};
newRover.getName = function() {
return name;
};
newRover.setDirection = function(newDirection) {
direction = newDirection;
};
newRover.getDirection = function() {
return direction;
};
newRover.setXCord = function(newXCord) {
xCord = newXCord;
};
newRover.getXCord = function() {
return xCord;
};
newRover.setYCord = function(newYCord) {
yCord = newYCord;
};
newRover.getYCord = function() {
return yCord;
};
newRover.where = function(){
return "Rover :: "+ name +" is at Location("+xCord+","+yCord+") pointing to "+direction;
};
return newRover;
};
rover1 = new roverObject();
rover2 = new roverObject();
rover1.setName("Mars Rover");
rover1.setDirection("NORTH");
rover1.setXCord(2);
rover1.setYCord(2);
console.log(rover1.where());
console.log(rover1);
rover2.setName("Moon Rover");
rover2.setDirection("SOUTH");
rover2.setXCord(1);
rover2.setYCord(1);
console.log(rover2.where());
console.log(rover2);
There are few questions that I have around this creation.
I want to create an object where the properties/attributes of object are private and not visible to world. Am I successful in doing that? Can I really not access the object attributes?
Is there a better way to create this kind of object?
If I want to inherit this object, I should do a newObject.prototype = roverObjectwill that work? And will that make sense most of all.
Finally I have a wierd problem. Notice the last method of objet "where" which returns a concatenated string. Here I tried following code instead.
newRover.where = function(){
return "Rover :: "+ name +" is at Location("+xCord+","+yCord+") pointing to "+direction;
}();
and then did a following console.log
console.log(rover1.where);
console.log(rover2.where);
It threw following error for me:
cannot access optimized closure
Why would it say that? What am I doing wrong?
Thanks for all the help. Any review comments would be appreciated too!
Cheers
Am I successful in doing that? Can I really not access the object attributes?
Indeed. You don't have object attributes, you have local variables in the roverObject function. Local variables can't be accessed from outside, only from the functions inside the roverObject function that have a closure over them.
That you are calling roverObject as a constructor, with new roverObject, is irrelevant, as you are returning a different object from the function. Saying var rover1= roverObject() without the new would do exactly the same thing. Notably the object returned by [new] roverObject is a plain Object as you created it from {}; rover1 instanceof roverObject is false.
If you wanted instanceof to work, you would have to call with new, and use this instead of newRover in the constructor function.
If I want to inherit this object, I should do a newObject.prototype = roverObject will that work? And will that make sense most of all.
No. You currently have no allowance for prototyping. You are using a separate copy of each method for each instance of the roverObject. You can do certainly objects this way but it's a different approach than prototyping. If you wanted to make something like a subclass of roverObject in the arrangement you have now, you'd say something like:
function AdvancedRover() {
var rover= new roverObject();
rover.doResearch= function() {
return rover.where()+' and is doing advanced research';
};
return rover;
}
Note since the ‘private’ local variables in the base class constructor really are private, even the subclass cannot get at them. There's no ‘protected’.
newRover.where = function(){ ... }();
What's that trying to do? I can't get the error you do; all the above does is assigns the string with the location to where (before the setter methods have been called, so it's full of undefineds).
Is there a better way to create this kind of object?
Maybe. see this question for a discussion of class/instance strategies in JavaScript.
Q1: you can create 'private' members in javascript 'classes'. In javascript, privacy is not determined by any access specifier. Instead, access needs to be specifically instrumented. Example:
function MyClass() {
this.val = 100; // public;
var privateVal = 200;
function getVal() { return this.val; } // private method;
this.getPrivateVal = function() { // public method, accessor to private variable
return privateVal;
}
}
Object scope in javascript is governed by a queer concept called closures. AFAIK, there is no parallel concept in any other popular launguage like C+/Java etc.
While I understand what closures are, I cannot put it in words. Perhaps a demonstration will help you:
function closureDemo() {
var done=false;
function setDone() { done=true; }
doLater(setDone);
}
function doLater(func) { setTimeout(func,1000); }
closureDemo();
now, while setDone is called from within doLater, it can still access done in closureDemo, even though done is not in scope (in the conventional procedural sense).
I think you will understand more when you read this.
Q2: I can only say what I do; I don't know if it is better or not. If I wrote your code, it would look like this:
function RoverObject() {
var newRover = {}; // privates
var name;
var xCord;
var ycord;
var direction;
this.setName = function(newName) {
name = newName;
};
this.getName = function() {
return name;
};
this.setDirection = function(newDirection) {
direction = newDirection;
};
// and so on...
this.where = function(){
return "Rover :: "+ name +" is at Location("+xCord+","+yCord+") pointing to "+direction;
};
}
var rover1 = new RoverObject();
Points to note:
capitalization of "class name"'s first letter
use of this instead of roverObject
this function is a pure constructor. it returns nothing.
Q3: if you want to do inheritance, then my method (use of this) will not work. Instead, the public methods should be a part of the prototype of RoverObject. Read this. Excellent material.
Hope that helps.
EDIT: There is a problem with the way your code is doing work. Problems:
your function does not do what its name suggests. Its name had better be createRoverObject, because that's exactly what it is doing. It is not working like a class constructor
the methods supported by your class are part of the object, but the data members are not. While this may work (and it is not, as your console.log() problem suggests), it is not a good way to implement a class in javascript. The problem here is of closures. Again, i'm unable to articulate what the problem specifically is, but I can smell it.
With regards to 4. - you are trying to log the function, not the result of calling the function. Should be console.log(rover1.where()); My guess firebug(I assume it's firebug's console.log) does not like to log function definitions.
EDIT Oh I get it, you are actually executing the where funcion when you assign rover.where. Are you trying to get what looks like a property to actually be a function? If that's the case it won't work. It will have to be a function if you want it to be evaluated when it's called.
What happens in you case where gets executed in the constructor function. At that point you are still creating the roverObject closure and hence it's too early to access it's private variables.
This is just addressing point 1 of your post.
Here's a good article on javascript private members and more:
Private Members in JavaScript
Defining your object like this gives you private members.
function RolloverObject() {
var name;
var xCord;
var ycord;
var direction;
this.setName = function(newName) { name = newName; };
this.getName = function() { return name; };
this.setDirection = function(newDirection) { direction = newDirection; };
this.getDirection = function() { return direction; };
this.setXCord = function(newXCord) { xCord = newXCord; };
this.getXCord = function() { return xCord; };
this.setYCord = function(newYCord) { yCord = newYCord; };
this.getYCord = function() { return yCord; };
this.where = function() {
return "Rover :: " + name + " is at Location(" + xCord + "," + yCord + ") pointing to " + direction;
};
}
var rolloverObject = new RolloverObject();