I got something for the javascript developers amongst us.
I got the following class:
function MyClass(){
this.__defineSetter__("array", function(val){
alert("setter called");
this._array = val;
});
this.__defineGetter__("array", function(){
alert("getter called");
return this._array;
});
this._array = new Array();
};
Now, what happens is that when I execute
var a = new MyClass();
a.array[0] = "MyString";
alert(a.array[0]);
the getter is called twice (which is fine), but the setter is never executed, as the actual array reference does not change, only the content (I guess expected behavior).
However, I'd also need to be "notified" when the array-content is modified. Thus, the call
a.array[0] = "MyString";
should also cause a setter-call (or something similar, important is to receive a notification when the array content has changed.
Anybody into this? How can this be achieved?
As we know,alert(a.array[0]); will only trigger a.array's getter/setter,and a.array[0] equal var p = a.array; p[0] which means what you want is trigger p[0]'s getter/setter,not just p's getter/setter.
So,we can change our mind to this thinking:
add getter/setter to all items of p
so,we can do it like this:
if some like p[6] = 0 is used , which will trigger p's getter/setter , judge if all item of p has getter/setter .if not add it.
if some like p = [2,3,4] is use , simply first set getter/setter to the value.
and the code is: Jsfiddle
Related
If I have this module pattern:
var MODULE = (function(window){
var myPublicStuff = {};
myPublicStuff.myPublicMethod = function(e){
return e;
};
return myPublicStuff;
})(window); //edit: forgot to put I have this
This works: (edited for clarity)
v = document.getElementById('some-element'); //works as expected
MODULE.myPublicMethod(v); //works.
But this does not work,
MODULE.myPublicMethod().document.getElementById('some-element');
or
document.getElementById('some-element').MODULE.myPublicMethod().
I thought that if the preceding member in the jail returned a value, you could chain it to the next link up? That does not work here, but I do not know why.
Edit: Thanks for all the answers. All I'm trying to do is get the element and have that method print it back out via chaining. That's all. if I put in 'btnPrint' I want it to give me <button type="button" id="btnPrint" class="btn">...</button> If I do getElementById at the console, that is what I get if I use a variable for my module first (which makes sense.) I only wanted to do the same thing with a chained method.
Edit: For completeness this is What Travis put on JSFiddle (thanks):
<button type="button" id="btnPrint" class="btn">...</button>
Element.prototype.myPublicMethod = function(){
//in the prototype scheme that JavaScript uses,
//the current instance of the Element is *this*,
//so returning this will return the current Element
//that we started with.
return this;
}
console.log(document.getElementById("btnPrint").myPublicMethod());
I agree. This looks bad unless absolutely necessary.
To avoid the v variable, you need to use
MODULE.myPublicMethod(document.getElementById('some-element'));
document is a global property (of the window object), you'd need to have returned that from myPublicMethod() to chain off it. Given that it is the identity function, you can even do something like
MODULE.myPublicMethod(document).getElementById('some-element');
MODULE.myPublicMethod(window).document.getElementById('some-element');
Is document.getElementById a method that can be chained?
Yes. It returns an Element (or undefined if there is no match). The Element exposes a generic set of functions, and if the element is a specific type (for example a form) then it may also have a specific set of functions exposed.
Read more on the generic Element type at https://developer.mozilla.org/en-US/docs/Web/API/Element
I want to do this:
v = document.getElementById('some-element'); //works as expected
MODULE.myPublicMethod(v);
Here, v is straightforward, right? It just gets the element with id="some-element". Okay, from there you pass it into the myPublicMethod(v). When you do that, all you are doing is calling a function that returns the same value that is passed in. And nothing else, there is no assignment or storing taking place in the code you show above.
What you could have done here, if you wanted to take advantage of the chaining setup, would be to then access the v element from the returned value like this:
v = document.getElementById('some-element');
var vId = MODULE.myPublicMethod(v).id;
console.log(vId);// this will log "some-element" to the console
But this does not work,
MODULE.myPublicMethod().document.getElementById('some-element');
So above I explained that you are "calling a function that returns the same value that is passed in", remember that myPublicMethod just has return e; in it? Well that means that you are using undefined as the result since nothing was passed in using this line of code. In other words, your code above can be examined as undefined.document.getElementById('some-element') which is hopefully clearly problematic.
if I put in 'btnPrint' I want it to give me <button type="button" id="btnPrint" class="btn">...</button>
For example, your code as written would accomplish this as such:
var MODULE = (function(window){
var myPublicStuff = {};
myPublicStuff.myPublicMethod = function(e){
return e;
};
return myPublicStuff;
})(window);
console.log(MODULE.myPublicMethod(document.getElementById('btnPrint')));
<button type="button" id="btnPrint" class="btn">...</button>
You can return the document from your function if no argument passed:
var MODULE = (function(window){
var myPublicStuff = {};
myPublicStuff.myPublicMethod = function(e){
return e || document;
};
return myPublicStuff;
})();
var text = MODULE.myPublicMethod().getElementById('element').innerHTML;
console.log(text);
JSBin
I was just doing a small test to see how Javascript would respond to changing a child object's value within a parent object. I wanted to see if the parent referenced the child and would keep up to date with the new values, or if it would only keep the initial state of the child that it was instantiated with.
I have the little module coords.js
var NS = NS || {};
NS.Coords = function(){
var __parentArray = {};
var __childArray = {};
addToParent = function(){
__childArray['value'] = "Initial";
__parentArray['child'] = __childArray;
},
showParent = function(){
console.log("Parent: ",__parentArray);
console.log("Child within parent: ",__parentArray['child']);
},
changeChild = function(){
__childArray['value'] = "Changed";
},
showChild = function(){
console.log("Child: ",__childArray]);
};
return {
addToParent: addToParent,
showParent: showParent,
changeChild: changeChild,
showChild: showChild
};
}();
And in main.js
var NS = NS || {};
// #codekit-prepend "Coords.js"
console.log("=============================");
console.log("Startpoint point");
console.log("=============================");
var coords = NS.Coords;
coords.addToParent();
coords.showChild();
coords.showParent();
console.log("=============================");
console.log("Changed child value");
console.log("=============================");
coords.changeChild();
coords.showChild();
coords.showParent();
If you run this, you see in the console that when shown directly, the child shows the expected "Initial" and then "Changed" values.
The parent, however, always shows the "Changed" value of the child object it references. Even before changeChild() is called. No idea why. Without even changing the value it shows that it's been changed. Am I missing something super simple, or am I misunderstanding what's going on here?
The first problem is you are probably using GOOGLE CHROME, i mean this isn't a problem, but don't forget that if you change a property, the console updates it AUTOMAGICALLY too. (Magical isn't it?)
If you change this console.log("Child within parent: ",__parentArray['child']); to console.log("Child within parent: ",__parentArray['child']['value']); then you can see that your script is working correctly.
Here is the fiddle: http://jsfiddle.net/M4Cd8/
There are some syntax errors in your code, but once those are fixed, the behavior you're describing does not occur. Some browsers' consoles provide an inline reference to objects, that is updated when the object changes. The answer is to observe the value string rather than the whole object:
console.log("Child within parent: ",__parentArray['child'].value);
console.log("Child: ",__childArray.value);
http://jsfiddle.net/pNtJJ/
But to clear up the question you were originally trying to figure out, yes, if you change an object that another object is referencing, then the change is refelcted no matter how you go about observing that, because a reference is precisely that - a link from one object to another.
As a side note, your variable names have the word "array" in them, but those are not arrays that you are working with, but objects. Arrays are typically created with square brackets [1, 2, 3, 4] and contain just values, not key-value pairs.
Which is the best way between:
var myClass = function() {
this.myContainer = function(){ return $(".container");}
this.mySubContainer = function(){ return this.myContainer().find(".sub"); }
}
AND
var myClass = function() {
this.myContainer = $(".container");
this.mySubContainer = this.myContainer.find(".sub");
}
Is there any concrete differences?
The memory problem arose when I have seen that my web page, that has enough javascript ( about 150KB of mine + libs ) takes more then 300-400MB of RAM. I'm trying to find out the problem and I don't know if this could be one of them.
function myClass{
this.myContainer = function(){ return $(".container");}
this.mySubContainer = function(){ return this.myContainer().find(".sub"); }
}
Here you will need to call it something like myClassInstance.myContainer() and that means jquery will search for .container element(s) any time you are using that function. Plus, it will create 2 additional closures any time you will create new instance of your class. And that will take some additional memory.
function myClass{
this.myContainer = $(".container");
this.mySubContainer = this.myContainer.find(".sub");
}
Here you will have myContainer pointing to an object which already contains all links to DOM nodes. jQuery will not search DOM any time you use myClassInstance.myContainer
So, second approach is better if you do not want to slow down your app. But first approach could be usefull if your DOM is frequently modified. But I do not beleave you have it modified so frequently that you may need to use second approach.
If there is a single variable you are trying to assign , then the second approach looks cleaner..
Yes they are different.
MODEL 1:
In the first model, myContainer is a function variable.It does not contain the jQuery object.You cannot call any of jQuery's methods on the objet. To actually get the jQuery object you will have to say
var obj = this.myContainer() or this.myContainer.call()
MODEL 2:
The second model stores the actual jQuery object.
try alerting this.myContainer in both models, u will seee the difference.
Yes this is different. after you fix your syntax error :
function myClass(){... // the parentheses
1st
When you create a new object var obj = new myClass(), you are creating two functions, and the jquery object is not returned until you call it
var container = obj.myContainer();
2nd
As soon as the object is initialized the dom is accessed and you have your objects cached for later use;
var container = obj.myContainer;
Is it possible to find the name of an anonymous function?
e.g. trying to find a way to alert either anonyFu or findMe in this code http://jsfiddle.net/L5F5N/1/
function namedFu(){
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var anonyFu = function() {
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var findMe= function(){
namedFu();
anonyFu();
}
findMe();
This is for some internal testing, so it doesn't need to be cross-browser. In fact, I'd be happy even if I had to install a plugin.
You can identify any property of a function from inside it, programmatically, even an unnamed anonymous function, by using arguments.callee. So you can identify the function with this simple trick:
Whenever you're making a function, assign it some property that you can use to identify it later.
For example, always make a property called id:
var fubar = function() {
this.id = "fubar";
//the stuff the function normally does, here
console.log(arguments.callee.id);
}
arguments.callee is the function, itself, so any property of that function can be accessed like id above, even one you assign yourself.
Callee is officially deprecated, but still works in almost all browsers, and there are certain circumstances in which there is still no substitute. You just can't use it in "strict mode".
You can alternatively, of course, name the anonymous function, like:
var fubar = function foobar() {
//the stuff the function normally does, here
console.log(arguments.callee.name);
}
But that's less elegant, obviously, since you can't (in this case) name it fubar in both spots; I had to make the actual name foobar.
If all of your functions have comments describing them, you can even grab that, like this:
var fubar = function() {
/*
fubar is effed up beyond all recognition
this returns some value or other that is described here
*/
//the stuff the function normally does, here
console.log(arguments.callee.toString().substr(0, 128);
}
Note that you can also use argument.callee.caller to access the function that called the current function. This lets you access the name (or properties, like id or the comment in the text) of the function from outside of it.
The reason you would do this is that you want to find out what called the function in question. This is a likely reason for you to be wanting to find this info programmatically, in the first place.
So if one of the fubar() examples above called this following function:
var kludge = function() {
console.log(arguments.callee.caller.id); // return "fubar" with the first version above
console.log(arguments.callee.caller.name); // return "foobar" in the second version above
console.log(arguments.callee.caller.toString().substr(0, 128);
/* that last one would return the first 128 characters in the third example,
which would happen to include the name in the comment.
Obviously, this is to be used only in a desperate case,
as it doesn't give you a concise value you can count on using)
*/
}
Doubt it's possible the way you've got it. For starters, if you added a line
var referenceFu = anonyFu;
which of those names would you expect to be able to log? They're both just references.
However – assuming you have the ability to change the code – this is valid javascript:
var anonyFu = function notActuallyAnonymous() {
console.log(arguments.callee.name);
}
which would log "notActuallyAnonymous". So you could just add names to all the anonymous functions you're interested in checking, without breaking your code.
Not sure that's helpful, but it's all I got.
I will add that if you know in which object that function is then you can add code - to that object or generally to objects prototype - that will get a key name basing on value.
Object.prototype.getKeyByValue = function( value ) {
for( var prop in this ) {
if( this.hasOwnProperty( prop ) ) {
if( this[ prop ] === value )
return prop;
}
}
}
And then you can use
THAT.getKeyByValue(arguments.callee.caller);
Used this approach once for debugging with performance testing involved in project where most of functions are in one object.
Didn't want to name all functions nor double names in code by any other mean, needed to calculate time of each function running - so did this plus pushing times on stack on function start and popping on end.
Why? To add very little code to each function and same for each of them to make measurements and calls list on console. It's temporary ofc.
THAT._TT = [];
THAT._TS = function () {
THAT._TT.push(performance.now());
}
THAT._TE = function () {
var tt = performance.now() - THAT._TT.pop();
var txt = THAT.getKeyByValue(arguments.callee.caller);
console.log('['+tt+'] -> '+txt);
};
THAT.some_function = function (x,y,z) {
THAT._TS();
// ... normal function job
THAT._TE();
}
THAT.some_other_function = function (a,b,c) {
THAT._TS();
// ... normal function job
THAT._TE();
}
Not very useful but maybe it will help someone with similar problem in similar circumstances.
arguments.callee it's deprecated, as MDN states:
You should avoid using arguments.callee() and just give every function
(expression) a name.
In other words:
[1,2,3].forEach(function foo() {
// you can call `foo` here for recursion
})
If what you want is to have a name for an anonymous function assigned to a variable, let's say you're debugging your code and you want to track the name of this function, then you can just name it twice, this is a common pattern:
var foo = function foo() { ... }
Except the evaling case specified in the MDN docs, I can't think of any other case where you'd want to use arguments.callee.
No. By definition, an anonymous function has no name. Yet, if you wanted to ask for function expressions: Yes, you can name them.
And no, it is not possible to get the name of a variable (which references the function) during runtime.
Below is my code fragment:
<div onclick = "myClick('value 1')">
button 1
</div>
<div onclick = "myClick('value 2')">
button 2
</div>
Basically when I for each click on a different div, a different value will be passed to the JavaScript function.
My Question is how can I keep track of the value passed in the previous click?
For example, I click "button 1", and "value 1" will be passed to the function. Later, I click on "button 2", I want to be able to know whether I have clicked "button 1" before and get "value 1".
Just add it to a variable in your script:
var lastClicked;
var myClick = function(value) {
lastClicked = value;
};
You can define somekind of variable, like var lastUsed;
add additional line to your function:
var lastUsed = null;
function myClick(value){
prevClicked = lastUsed; //get the last saved value
...
lastUsed = value; //update the saved value to the new value
...
}
And here you go
You need a variable. Variables are like little boxes in which you can store values. In this case, we can store the value that was last passed to the function myClick.
In Javascript, you can define a variable like this:
var lastClickedValue;
You can "put" a value into that variable. Let's say you want to put your name in there. You would do this:
lastClickedValue = 'sams5817';
Now here's the tricky bit. Variables have "scope". You might want to think about it as their "life-time". When a variable reaches the end of its scope, you cannot read or write to it anymore. It's as if it's never been. Functions define a scope. So any variable you define in a function will disappear at the end of the function. For example:
function myClick(value)
{
var lastClickedValue;
alert('lastClickedValue is = ' + value);
lastClickedValue = value;
}
That looks almost right, doesn't it? We declared a variable, display its last value, and update it with the new value.
However, since the lastClickedValue was declared in the function myClick, once we've reached the end of that function, it's gone. So the next time we call myClick, lastClickedValue will be create all over again. It will be empty. We call that an "uninitialized" variable.
So what's the problem? We're trying to remember a value even after the end of myClick. But we declared lastClickedValue inside myClick, so it stops existing at the end of myClick.
The solution is to make sure that lastClickedValue continues to exist after myClick is done.
So we must delcare lastClickedValue in a different scope. Luckily, there's a larger scope called the "global scope". It exists from the moment your page loads, and until the user moves on to another webpage. So let's do it this way:
var lastClickedValue;
function myClick(value)
{
alert('lastClickedValue is = ' + value);
lastClickedValue = value;
}
It's a very small difference. We moved the declaration of the variable lastClickedValue to be outside the function myClick. Since it's outside, it will keep existing after myClick is done. Which means that each time we call myClick, then lastClickedValue will still be there.
This will let you know what the last value passed to myClick was.
Finally, I'd like to advise you to look for some kind of Javascript tutorials. I wish I knew of some good ones to recommend, but I'm certain you can find a few on the Internet. If you try to write programs before understanding what you're doing, you'll find yourself producing work that is less than what you're capable of. Good luck!
I suppose you need something like this
var clickedButtons = [];
function myClick(value){
...
clickedButtons.push(value);
...
}
I am surprised that no one else mentioned this, but since functions are first class objects in JavaScript, you can also assign attributes and methods to functions. So in order to remember a value between function calls you can do something like I have with this function here:
function toggleHelpDialog() {
if (typeof toggleHelpDialog.status === 'undefined')
toggleHelpDialog.status = true;
else
toggleHelpDialog.status = !toggleHelpDialog.status;
var layer = this.getLayer();
if (toggleHelpDialog.status) layer.add(helpDialog);
else helpDialog.remove();
layer.draw();
}
Here I have added an attribute named 'status' to the toggleHelpDialog function. This value is associated with the function itself and has the same scope as the toggleHelpDialog function. Values stored in the status attribute will persist over multiple calls to the function. Careful though, as it can be accessed by other code and inadvertently changed.
we can leverage javascript static variables
One interesting aspect of the nature of functions as objects is that you can create static
variables. A static variable is a variable in a function‘s local scope whose value persists across
function invocations. Creating a static variable in JavaScript is achieved by adding an instance
property to the function in question. For example, consider the code here that defines a function
doSum that adds two numbers and keeps a running sum:
function doSum(x,y){
if (typeof doSum.static==='undefined'){
doSum.static = x+y;
}else{
doSum.static += x+y;
}
if (doSum.static >= 100){doSum.static = 0;doSum.static += x+y;}
return doSum.static;
}
alert(doSum(5,15))
alert(doSum(10,10))
alert(doSum(10,30))
alert(doSum(20,30))