PDA

View Full Version : Passing, things...



???
12-13-2008, 09:01 PM
Let me give some examples before I pose my question. If I do:


var thing1 = "thing1";
var func = function () {
alert (thing1);
}
func ();

It will alert "thing1". Right? Right. But then, if I do:


var set = function () {
var thing1 = "thing1";
func ();
}
var func = function () {
alert (thing1);
}
set ();

It will not alert "thing1". I'm assuming that the first one works because the function is being defined in the same scope as where the variable is defined, and it doesn't matter where it is called from. Ok, first question now. How do I make the second example work? I could change the scope using "call ()" or "apply ()", but that would require me to do something like:


var set = function () {
var obj = new Object ();
obj.thing1 = "thing1";
func.call (obj);
}
var func = function () {
alert (this.thing1);
}
set ();

Is there anyway to do it without having to use "call ()" or "apply ()" and "this.myProp"? I know I being a little picky, and if there's no other way I'll use the "this.myPro", but I'd rather not. It also would cause a small nastiness in my code that I would rather not have. In case you want to know, I'm creating a Class class and attempting to use inheritance and have public and private variables.

Any help would be appreciated,
Stephen

jscheuer1
12-13-2008, 10:41 PM
One of the many Object Oriented approaches will be quite effective here (there are many other ways):


var set = function () {
set.thing1 = 'thing1';
func ();
},
func = function () {
alert (set.thing1);
};
set ();

Here's another way:


var Set = function () {
this.thing1 = 'thing1';
this.func ();
};
Set.prototype.func = function () {
alert (this.thing1);
};
new Set ();

Or:


var Set = function () {
this.thing1 = 'thing1';
func (this);
};
var func = function (s) {
alert (s.thing1);
};
new Set ();

Or:


var Set = function () {
this.thing1 = 'thing1';
};
var func = function () {
alert (a_set.thing1);
};
var a_set = new Set ();
func ();

Or:


var set = {
thing1 : 'thing1',
func : function () {
alert (this.thing1);
}
};
set.func ();

???
12-14-2008, 03:23 PM
Is there anyway to pass "thing1" on as a variable, or does it have to be a property. As in, do I have to do "someObject.thing1"?

jscheuer1
12-14-2008, 03:54 PM
Well, though you can redefine an object or create a new object of the same name at a different scope, you cannot change the scope of an object. So this will work (func and thing1 in the set function's scope):


var set = function () {
var func = function () {
alert (thing1);
},
thing1 = 'thing1';
func ();
};
set ();

as will (func and thing1 in the global scope):


var thing1,
set = function () {
thing1 = 'thing1';
func ();
},
func = function () {
alert (thing1);
};
set ();

A lot depends upon your goals and requirements as they relate to the code. A good rule of thumb is to only expose one object or less to the global scope. If your code doesn't need to access the global scope or to be accessed from it, expose nothing. If access one way, the other, or both is required, create one master object that can grant access to whatever parts of your code require it.

One example of no global exposure:


(function () {
var thing1,
set = function () {
thing1 = 'thing1';
func ();
},
func = function () {
alert (thing1);
};
set ();
})();

The other criterion would be the coding environment. If you have complete control over that, expose as much as you dare. However, if others will be using the code in various environments, or if you want to be able to add code to your environment without thinking too much about what's already in the global scope, follow my first recommendations as regards exposure.

???
12-14-2008, 04:31 PM
Ok, let me just show you my code. To tell the truth, I always get lost in this scope stuff. I am creating a "Class" class. Originally, I started with defining all the properties and methods in my classes in the constructor, thus making it so if I defined an object "private" at the head of the constructor, nothing could access it except the constructor and the methods defined therein. Also, to make it nicer, I created a "public" variable that I set equal to "this", additionally getting around future "this" problems. Then I got to the problem of inheritance. What I decided to do was rather than creating a new one of each of the parent classes and stealing all the properties (wouldn't work for the private ones anyway), I would just pass the parent classes the new class's "private" and "public" variables, so the would add on themselves. Right now, I'm at the point where the parents can add to it if they do "this.private" or "this.public". This is my code:


//Used to convert "arguments" which isn't actually an array into one
Array.toArray = function (array) {
var arr = new Array ();
for (var i = 0; i < array.length; i = i + 1) {
arr [i] = array [i];
}
return arr;
};

var Class = function () {
var parents = Array.toArray (arguments);
parents.reverse ();
return Object (function () {
var props = new Object ();
props.inherit = true;
if (typeof this.inherit != "undefined") {
props.private = private;
props.public = public;
}
else {
props.private = new Object ();
props.public = this;
}
for (var i = 0; i < parents.length; i = i + 1) {
parents [i].apply (props,new Array ());
}
});
};
//Testing
var tst = new Class (function () {
this.public.t = "...";
});
var t = new tst ();
alert (t.t);

Explaining what I'm doing:
"Class" takes in functions to be run. It flips them, so the earlier ones functions take priority over the later ones. The thing with the return and object make it so that when "Class" finishes running, whatever it was being created for just becomes the function that is being returned inside the "Object ()". As in:


var const = function () {
return Object (function () {
alert ("test");
});
};
//This:
var test = new const ();
//Would be the same as:
var test = function () {
alert ("test");
};

Then, as you can see, when the resulting function is run, it executes all the functions passed to the original "Class" constructor and sets their scopes to be "props" which has "inherit", "public", and "private". Now this is all fine and dandy, but the person making the class would have to do "this.private" or "this.public". Basically, my question (if you're not already asleep) is if I can, how do I remove the "this."? Also, is this too lame a little detail for me to worry about?

Sorry if I was unclear before or am now, and thanks for any help you can give,
Stephen

jscheuer1
12-15-2008, 04:32 AM
I have to admit that this latest code is a bit out of my league. I focus more on practical application, while this appears to be the rudiment of some sort of library of code like prototype, mootools, or jQuery.

I might be able to be of more help and/or learn something if you showed me something you actually want to do with this. I've not yet gotten to the stage where abstract code does much for me. I do appreciate libraries, but often whatever can be done with them can be done more simply and accurately with a more basic script.

Just to be clear, it's fine with me whatever you are trying to do (though I would caution you that FF's error console issued a warning on your use of public and private, as these are reserved. If you are using them for their intended purpose, fine. However, 'reserved' usually means that they just aren't allowed yet because some future use is, well - reserved).

Are you formulating a library, or has this a more specific purpose? In any case, extending objects is done in prototype, and (I think) mootools, perhaps even jQuery, though jQuery is less ambitious, so may not attempt that. You might get a lot of the information you are after by studying how other libraries that do this sort of thing accomplish it. Their source code and documentation is available on the web and is more or less in the public domain.

One thing I can tell you about extending objects is that it can get messy when you (or someone else) later goes to access the extended object's properties looking for native properties only (hasOwnProperty is a workaround for that, though is relatively recent, so older browsers won't support it, and less savvy coders won't know to use it for those browsers that do).

On another tack, I can tell you that both Twey and Trinithis (members here), if they are interested, would probably be more able to discuss this sort of code at the level of abstraction that seems to engage you.

Feel free to PM them and tell them that I 'sent you'. But they aren't around as much as I am here, so be patient, and also understand if they don't choose to get involved.

Twey
12-15-2008, 06:40 AM
Javascript, like most modern languages, has lexical scoping: a function has access to the variables available in the scope where it is defined, not where it's called. The usual response to something like this is to pass in the property collections as arguments:
function Class() {
return function() {
var props = this.inherit !== undefined
? { inherit: true, public: public, private: private }
: { inherit: true, public: this, private: {} };

for (var i = arguments.length; --i >= 0; )
arguments[i].call(props, public, private);
};
}Then:
var tst = new Class(function(public) {
public.t = "...";
});However, whether this should be done is a fairly large question. If this is an exercise, then by all means go ahead: coding a class-based OO system in a prototypical OO language is an interesting way to learn about both. On the other hand, if this is meant to be at all practical, I recommend you drop it. A lot of people, coming to Javascript from class-based languages like Java or C++, have an immediate reaction of 'argh! What is this crap! Why won't it do classes!' However, Javascript's prototypical OO model, despite its share of flaws, is far more powerful than a class-based model. You should learn to use it, not put clown paint on a ballerina.

Miscellaneous notes:Using the function statement rather than a variable assignment is generally preferable, where possible, since the assignment and naming can be done at once (you omitted to name your function in your original code, and named function expressions are frowned upon besides, due to performance issues in certain interpreters). Array.toArray is Array.prototype.slice.call. Putting spaces before the brackets of function calls or the square brackets of property lookups is frowned upon: the additional space before the brackets is conventionally reserved to distinguish keywords from functions (in many languages, not just Javascript). Separating function arguments with spaces, on the other hand, is encouraged. Javascript provides syntactic sugar for new Object() and new Array() that is terser and more powerful. Use it. In combination with the ternary operator, in this case, it makes your code a lot simpler. Reversing an array is an expensive operation. If you're going to iterate over it anyway, just iterate over it backwards in the first place. Iterating over arrays backwards is simpler anyway, and should be preferred when order doesn't matter. Don't use tab characters in your code. Different systems render tabs differently, so you can never be certain just how your code will look (in my case, I got a huge eight-space tab). Editing with tabs can be useful (although personally I dislike it), but have your editor expand them on saving. Four spaces per level is sufficient to make the structure of the code clear without making the lines huge and ungainly, and is standard in several languages and libraries. If you don't intend to pass a variable number of arguments from an array, call() is more efficient than apply(). call() works in a similar way, but takes its arguments directly rather than through an intermediate array (and the arguments can be omitted entirely if unneeded).

jscheuer1
12-15-2008, 07:03 AM
Javascript's prototypical OO model, despite its share of flaws, is far more powerful than a class-based model. You should learn to use it, not put clown paint on a ballerina.

LOL, I knew we kept you around here for a reason. :D

Twey
12-15-2008, 07:09 AM
It was difficult to find an inverse analogy to 'lipstick on a pig' :p

???
12-16-2008, 04:16 PM
Clarification and wrap up...
In the end, I have to either pass "public" and "private" as arguments, or "props" as the scope so that they're "this.private" and "this.public". Right? I might be wrong... As to whether it should be done, I'd like to have easy inheritance for some class (or prototypes?) of mine, and I've found that private variables are always nice. Rather than


var myFunc = function () {...

I should do


function myFunc () {...

unless it's a property, when I have to do the first.
The slice thing, is it like


Array.prototype.slice.call (arguments,0);

? So it returns an array with the elements of arguments... I think.

Thanks! I've gotta go to school, so I'll confirm the rest later,
Stephen

Twey
12-17-2008, 12:08 AM
In the end, I have to either pass "public" and "private" as arguments, or "props" as the scope so that they're "this.private" and "this.public". Right?Right. This is the boiling issue of Javascript design: use the OO method (properties) or the functional method (arguments)? In practice, the functional method seems to always be better, since Javascript's OO is somewhat borked in a few ways (unfortunately, not ones you can readily fix by implementing another layer on top).
As to whether it should be done, I'd like to have easy inheritance for some class (or prototypes?) of mineWhich you can get using the prototypical method.
and I've found that private variables are always nice.They make sense in static languages, but a lot less in dynamic languages. The Python approach applies to Javascript: stop assuming your users are stupid. If they shouldn't be messing with a property, then just tell them so in the documentation (there's also a convention of prefixing such properties with underscores).
The slice thing, is it likeYes. The key is that the implementation of slice() doesn't require anything except .length and numerical indices, so it needn't be called on an array (it should be possible to call it on anything that implements those, but unfortunately IE complains if you call it on a non-JScript object [e.g. DOM NodeLists, which are implemented in COM]).