I am struggling to understand how this JavaScript code work. I am learning JS, and not exposed to a dynamic, functional language before. So, I visualize function calls in bit procedural, hierarchical order. With d3.js, one can draw svg elements as explained here
var dataset = [ 5, 10, 15, 20, 25 ];
d3.select("body").selectAll("p")
.data(dataset)
.enter()
.append("p")
.text("New paragraph!");
Let’s change the last line:
.text(function(d) { return d; });
Check out what the new code does on this demo page.
Whoa! We used our data to populate the contents of each paragraph, all thanks to the magic of the data() method. You see, when chaining methods together, anytime after you call data(), you can create an anonymous function that accepts d as input. The magical data() method ensures that d is set to the corresponding value in your original data set, given the current element at hand.
This magic, mentioned above is what I fail to understand. "d" is not a global variable, as if I change to another (c) name, it still works. So, the data method may be setting the value for the anonymous fn.
But, typically(with my limited reading) chaining is possible because the current function returns an object, on which the next method can be invoked. In the above case, how data method knows whether a text ("New paragraph!") is passed by the user, otherwise pass the data to the anonymous fn. The doubt is, the text method is down the line and data() is already executed. How the data is passed to the anonymous function?
thanks.
Digging into d3.js internals shows the following result for text function:
d3_selectionPrototype.text = function(value) {
return arguments.length < 1
? this.node().textContent : this.each(typeof value === "function"
? function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; } : value == null
? function() { this.textContent = ""; }
: function() { this.textContent = value; });
};
In case the supplied argument is a function, the following code gets executed:
this.each(function() {
var v = value.apply(this, arguments); // executing function provided
this.textContent = v == null ? "" : v;
});
Function each is declared as:
d3_selectionPrototype.each = function(callback) {
for (var j = -1, m = this.length; ++j < m;) {
for (var group = this[j], i = -1, n = group.length; ++i < n;) {
var node = group[i];
if (node) callback.call(node, node.__data__, i, j); // this is the line you are interested in
}
}
return this;
};
so on each invocation it supplies an element from this. And, getting down to it, this is populated by data function invocation.
Well, I have never used d3 before, but this is what I understand.
d is the data object (I would call it data instead of d had set in the data() method.
So what does the text() method does? Will it will call the function and use it's output, something like this:
function text (callback) {
var theText;
if (typeof callback === "function") {
theText = callback(dataset);
} else {
theText = callback;
}
// does something more
}
So, if callback is a function call it, and use its return value as text.
Then, what I'm guessing, is that if the function is an array, it will call the text method for each element in the array.
Something like this...
function text(callback) {
var theText;
if (typeof callback === "function") {
theText = callback(dataset);
} else {
theText = callback;
}
if (theText instanceof Array) { // this is not the best way to check if an object is an array, I'll come back to this later. I'm sorry.
for (var i=0, len=theText.length; i<len; i++) {
text(theText[i]);
}
} else {
// do something else
}
// do something more
}
please take into account that this would be a really simple version of what really happens.
If it's not clear enough please let me know.
Related
I need to do some experiment and I need to know some kind of unique identifier for objects in javascript, so I can see if they are the same. I don't want to use equality operators, I need something like the id() function in python.
Does something like this exist ?
Update My original answer below was written 6 years ago in a style befitting the times and my understanding. In response to some conversation in the comments, a more modern approach to this is as follows:
(function() {
if ( typeof Object.id != "undefined" ) return;
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
console.log(Object.id(function() {}));
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`
If you have archaic browser requirements, check here for browser compatibility for Object.defineProperty.
The original answer is kept below (instead of just in the change history) because I think the comparison is valuable.
You can give the following a spin. This also gives you the option to explicitly set an object's ID in its constructor or elsewhere.
(function() {
if ( typeof Object.prototype.uniqueId == "undefined" ) {
var id = 0;
Object.prototype.uniqueId = function() {
if ( typeof this.__uniqueid == "undefined" ) {
this.__uniqueid = ++id;
}
return this.__uniqueid;
};
}
})();
var obj1 = {};
var obj2 = new Object();
console.log(obj1.uniqueId());
console.log(obj2.uniqueId());
console.log([].uniqueId());
console.log({}.uniqueId());
console.log(/./.uniqueId());
console.log((function() {}).uniqueId());
Take care to make sure that whatever member you use to internally store the unique ID doesn't collide with another automatically created member name.
So far as my observation goes, any answer posted here can have unexpected side effects.
In ES2015-compatible enviroment, you can avoid any side effects by using WeakMap.
const id = (() => {
let currentId = 0;
const map = new WeakMap();
return (object) => {
if (!map.has(object)) {
map.set(object, ++currentId);
}
return map.get(object);
};
})();
id({}); //=> 1
Latest browsers provide a cleaner method for extending Object.prototype. This code will make the property hidden from property enumeration (for p in o)
For the browsers that implement defineProperty, you can implement uniqueId property like this:
(function() {
var id_counter = 1;
Object.defineProperty(Object.prototype, "__uniqueId", {
writable: true
});
Object.defineProperty(Object.prototype, "uniqueId", {
get: function() {
if (this.__uniqueId == undefined)
this.__uniqueId = id_counter++;
return this.__uniqueId;
}
});
}());
For details, see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
Actually, you don't need to modify the object prototype and add a function there. The following should work well for your purpose.
var __next_objid=1;
function objectId(obj) {
if (obj==null) return null;
if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
return obj.__obj_id;
}
For browsers implementing the Object.defineProperty() method, the code below generates and returns a function that you can bind to any object you own.
This approach has the advantage of not extending Object.prototype.
The code works by checking if the given object has a __objectID__ property, and by defining it as a hidden (non-enumerable) read-only property if not.
So it is safe against any attempt to change or redefine the read-only obj.__objectID__ property after it has been defined, and consistently throws a nice error instead of silently fail.
Finally, in the quite extreme case where some other code would already have defined __objectID__ on a given object, this value would simply be returned.
var getObjectID = (function () {
var id = 0; // Private ID counter
return function (obj) {
if(obj.hasOwnProperty("__objectID__")) {
return obj.__objectID__;
} else {
++id;
Object.defineProperty(obj, "__objectID__", {
/*
* Explicitly sets these two attribute values to false,
* although they are false by default.
*/
"configurable" : false,
"enumerable" : false,
/*
* This closure guarantees that different objects
* will not share the same id variable.
*/
"get" : (function (__objectID__) {
return function () { return __objectID__; };
})(id),
"set" : function () {
throw new Error("Sorry, but 'obj.__objectID__' is read-only!");
}
});
return obj.__objectID__;
}
};
})();
Typescript version of #justin answer, ES6 compatible, using Symbols to prevent any key collision and added into the global Object.id for convenience. Just copy paste the code below, or put it into an ObjecId.ts file you will import.
(enableObjectID)();
declare global {
interface ObjectConstructor {
id: (object: any) => number;
}
}
const uniqueId: symbol = Symbol('The unique id of an object');
export function enableObjectID(): void {
if (typeof Object['id'] !== 'undefined') {
return;
}
let id: number = 0;
Object['id'] = (object: any) => {
const hasUniqueId: boolean = !!object[uniqueId];
if (!hasUniqueId) {
object[uniqueId] = ++id;
}
return object[uniqueId];
};
}
Example of usage:
console.log(Object.id(myObject));
jQuery code uses it's own data() method as such id.
var id = $.data(object);
At the backstage method data creates a very special field in object called "jQuery" + now() put there next id of a stream of unique ids like
id = elem[ expando ] = ++uuid;
I'd suggest you use the same method as John Resig obviously knows all there is about JavaScript and his method is based on all that knowledge.
For the purpose of comparing two objects, the simplest way to do this would be to add a unique property to one of the objects at the time you need to compare the objects, check if the property exists in the other and then remove it again. This saves overriding prototypes.
function isSameObject(objectA, objectB) {
unique_ref = "unique_id_" + performance.now();
objectA[unique_ref] = true;
isSame = objectB.hasOwnProperty(unique_ref);
delete objectA[unique_ref];
return isSame;
}
object1 = {something:true};
object2 = {something:true};
object3 = object1;
console.log(isSameObject(object1, object2)); //false
console.log(isSameObject(object1, object3)); //true
I faced the same problem and here's the solution I implemented with ES6
code
let id = 0; // This is a kind of global variable accessible for every instance
class Animal {
constructor(name){
this.name = name;
this.id = id++;
}
foo(){}
// Executes some cool stuff
}
cat = new Animal("Catty");
console.log(cat.id) // 1
I've used code like this, which will cause Objects to stringify with unique strings:
Object.prototype.__defineGetter__('__id__', function () {
var gid = 0;
return function(){
var id = gid++;
this.__proto__ = {
__proto__: this.__proto__,
get __id__(){ return id }
};
return id;
}
}.call() );
Object.prototype.toString = function () {
return '[Object ' + this.__id__ + ']';
};
the __proto__ bits are to keep the __id__ getter from showing up in the object. this has been only tested in firefox.
Notwithstanding the advice not to modify Object.prototype, this can still be really useful for testing, within a limited scope. The author of the accepted answer changed it, but is still setting Object.id, which doesn't make sense to me. Here's a snippet that does the job:
// Generates a unique, read-only id for an object.
// The _uid is generated for the object the first time it's accessed.
(function() {
var id = 0;
Object.defineProperty(Object.prototype, '_uid', {
// The prototype getter sets up a property on the instance. Because
// the new instance-prop masks this one, we know this will only ever
// be called at most once for any given object.
get: function () {
Object.defineProperty(this, '_uid', {
value: id++,
writable: false,
enumerable: false,
});
return this._uid;
},
enumerable: false,
});
})();
function assert(p) { if (!p) throw Error('Not!'); }
var obj = {};
assert(obj._uid == 0);
assert({}._uid == 1);
assert([]._uid == 2);
assert(obj._uid == 0); // still
This one will calculate a HashCode for each object, optimized for string, number and virtually anything that has a getHashCode function. For the rest it assigns a new reference number.
(function() {
var __gRefID = 0;
window.getHashCode = function(ref)
{
if (ref == null) { throw Error("Unable to calculate HashCode on a null reference"); }
// already cached reference id
if (ref.hasOwnProperty("__refID")) { return ref["__refID"]; }
// numbers are already hashcodes
if (typeof ref === "number") { return ref; }
// strings are immutable, so we need to calculate this every time
if (typeof ref === "string")
{
var hash = 0, i, chr;
for (i = 0; i < ref.length; i++) {
chr = ref.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0;
}
return hash;
}
// virtual call
if (typeof ref.getHashCode === "function") { return ref.getHashCode(); }
// generate and return a new reference id
return (ref["__refID"] = "ref" + __gRefID++);
}
})();
If you came here because you deal with class instances like me you can use static vars/methods to reference instances by a custom unique id:
class Person {
constructor( name ) {
this.name = name;
this.id = Person.ix++;
Person.stack[ this.id ] = this;
}
}
Person.ix = 0;
Person.stack = {};
Person.byId = id => Person.stack[ id ];
let store = {};
store[ new Person( "joe" ).id ] = true;
store[ new Person( "tim" ).id ] = true;
for( let id in store ) {
console.log( Person.byId( id ).name );
}
Here's a variant of Justin Johnson's answer that provides a scalability benefit when you are creating billions of objects for which you want the ID.
Specifically, rather than solely using a 1-up counter (that might overflow the representational limits of Number, and can't be cycled without risking reusing an ID), we register the object and its newly generated ID with a FinalizationRegistry, such that, at some point after the object is garbage collected, the ID is returned to a freelist for reuse by a newly created object (Python's id function can also return the same ID for multiple objects, so long as the existence of the two objects does not overlap in time).
Limitations:
It only works on objects, not JS primitives (this is somewhat reasonable; unlike Python, where everything is an object, JS primitives typically aren't, and the id function logically only works on objects, since primitives need not "exist" in any reasonably identifiable way).
If the code creates (without discarding) billions of objects, asks for their IDs, then releases them all at once and never asks for an ID again, the recovered IDs in the freelist constitute a memory leak of sorts. Hopefully the JS optimizer stores them efficiently, so the cost remains a small fraction of what the objects themselves cost, but it's still a cost. In cases where objects with IDs are regularly created and destroyed, the wasted memory is roughly tied to the maximum number of such ID-ed objects in existence at any given point in time.
If those limitations aren't a problem though, this works fairly well. I modified the testing code a bit to hand control back to the event loop (and hopefully the garbage collector) now and again while creating 10M garbage objects to ID, and on my browser, nearly half the object IDs get reclaimed for reuse; the final loop making five objects and IDing them produces IDs just above 1M, when over 2M objects had IDs generated at some point. In a realistic scenario with meaningful code executing and real async usage I'd expect better results simply because there would be more opportunities for the finalization registry to perform cleanup.
async function sleep(ms) {
await _sleep(ms);
}
function _sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
(function() {
if ( typeof Object.id != "undefined" ) return;
var freelist = []; // Stores previously used IDs for reuse when an object with
// an ID is garbage collected, so creating and dropping billions
// of objects doesn't consume all available IDs
const registry = new FinalizationRegistry((freeid) => {
freelist.push(freeid);
});
var id = 0;
Object.id = function(o) {
if ( typeof o.__uniqueid != "undefined" ) {
return o.__uniqueid;
}
Object.defineProperty(o, "__uniqueid", {
value: freelist.length ? freelist.pop() : ++id,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
registry.register(o, o.__uniqueid); // Sometime after o is collected, its ID
// will be reclaimed for use by a new object
return o.__uniqueid;
};
})();
var obj = { a: 1, b: 1 };
console.log(Object.id(obj));
console.log(Object.id([]));
console.log(Object.id({}));
console.log(Object.id(/./));
var idsum = 0; // So we do something real to prevent optimizing out code
// Make a ton of temporary objects with IDs, handing control back to the event loop
// every once in a while to (hopefully) see some IDs returned to the pool
for (var i = 0; i < 1000000; ++i) {
idsum += Object.id({c: i});
}
sleep(10).then(() => {
console.log(Object.id(function() { console.log("Hey"); }));
for (var i = 1000000; i < 2000000; ++i) {
idsum += Object.id({c: i});
}
console.log(Object.id(function() { console.log("There"); }));
sleep(10).then(() => {
for (var i = 0; i < 5; ++i) {
console.log(Object.id([i]));
}
console.log(idsum);
});
});
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
console.log(k);
}
}
// Logged keys are `a` and `b`
So, I have this little code in my js file:
window.onload = function Equal() {
var a = 'b1'
var b = 'box1'
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
var a = 'b2'
var b = 'box2'
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
}
The function itself is not important (it equals checkboxvalues set in the localstorage), but I execute it 2 times. First time with var a & b set to 'b1' & 'box1'. Then I run the script again (same script), but with var a & b set to 'b2' & 'box2'. Now, this code works, but my question is if there is a shorter way to write this? I can imagine some sort of array with a loop, but I could not get it to work for some reason. The 2 variables are pairs, and I know this might be a dumb question, but I can't find the answer anywhere.
You can use a second function which will accept the local storage key and the checkbox id like
window.onload = function Equal() {
setCheckboxState('box1', 'b1');
setCheckboxState('box2', 'b2');
}
function setCheckboxState(id, key) {
document.getElementById(id).checked = 1 == localStorage.getItem(key);
}
You might separate common logic into another function
window.onload = function Equal() {
function extractFromStorage(a, b) {
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
}
extractFromStorage('b1', 'box1');
extractFromStorage('b2', 'box2');
}
function doTheStuff(a, b) {
var bookstorname = localStorage.getItem(a)
if (bookstorname == 1) {
document.getElementById(b).setAttribute('checked','checked');
}
if (bookstorname == 0) {
document.getElementById(b).removeAttribute('checked','checked');
}
}
window.onload = function Equal() {
doTheStuff('b1', 'box1');
doTheStuff('b2', 'box2');
}
?
This is how I would do it.
There are several problems with your code.
You do not check that the element you are stetting an attribute to
exists. You do not check if the localStorage item you get is
defined.
You pollute the global name space with the function name Equal.
That function should not be named with a capital as it is not a Object generator.
There is no need to use setAttribute and removeAttribute, in
fact removeAttribute makes no sense in this case as you can not
remove the checked attribute from the element. BTW why use setAttribute here and not for window.onload?
The checked attribute is either true or false, it does not use the
string "checked"
Binding the load event via the onload attribute is not safe as you may
block 3rd party code, or worse 3rd party code may block you.
There is no error checking. DOM pages are dynamic environments, pages
have adverts and content from many places that can interfer with your
code. Always code with this in mind. Check for possible errors and deal with them in a friendly way for the end user. In this case I used an alert, not friendly for a normal user but for you the coder.
My solution.
// add an event listener rather than replace the event listener
window.addEventListener(
"load", // for the load event
function(){
// the update function that is called for each item;
var update = function(item){
// the right hand side equates to true if the localstorage
// is equal to "1". LocalStorage allways returns a string or
// undefined if the key is not defined.
item.element.checked = localStorage[item.storageName] === "1";
}
// safe element getter
var getElement = function(eId){
var e = document.getElementById(eId); // try and get the element
if(e === null){ // does it exist?
throw "Missing element:"+eId; // no then we can not continue
// the program stops here unless
// you catch the error and deal with
// it gracefully.
}
return e; //ok return the element.
}
// Item creator. This creates a new item.
// sName is the local storage name
// eId id the element ID
var item = function(sName, eId){
return {
storageName: sName, // set the loaclStorage name
element:getElement(eId); // get the element and check its safe
};
}
// make it all safe
try{
// create an array of items.
var items = [
item("b1","box1"),
item("b2","box2")
];
// for each item update the element status
items.forEach(update);
}catch(e){
alert("Could not update page?");
}
}
);
I would like to measure the computing time of methods.
A nice way is (How do you performance test JavaScript code?) with console.time('Function #1'); and console.timeEnd('Function #1');
My idea is to add these console outputs on lifecycle-methods. In this case using SAPUI5 like createContent:funtion(){}; methods.
This should be possible with AOP using before() and after() to runt the time counting.
Which AOP framework would you suggest and how to implement it with the need of modifying the identification string "Function #1" automatically?
There actually is no need for aspects in Javascript since you can change any function of any object at any time. JavaScript prototypes allows you to manipulate method implementations of all instances of an object at runtime. Here are two approaches for what you plan.
You could use a generic wrapper function:
var measureId = 0;
var fnMeasureFunction = function(fnToMeasure) {
console.time('measure'+ measureId);
fnToMeasure();
console.timeEnd('measure'+ measureId);
measureId++;
}
Admittedly that requires you to change your actual code...
For static functions or functions that belong to a prototype you could also do sth. like this from the outside without the need of any change to your existing code:
// any static function
var measureId = 0;
var fnOriginalFunction = sap.ui.core.mvc.JSViewRenderer.render;
sap.ui.core.mvc.JSViewRenderer.render = function() {
console.time('measure'+ measureId);
fnOriginalFunction.apply(this, arguments);
console.timeEnd('measure'+ measureId);
measureId++;
}
// any prototype function
var fnOriginalFunction = sap.m.Button.prototype.ontouchstart;
sap.m.Button.prototype.ontouchstart= function() {
console.time('measure'+ measureId);
fnOriginalFunction.apply(this, arguments);
console.timeEnd('measure'+ measureId);
measureId++;
}
This should be possible with AOP using before() and after() to runt the time counting.
As it already got mentioned, one really is not in need of real Aspect-oriented Programming
in order to solve such tasks in JavaScript. But this language might deserve some more standardized
method-modifiers in addition to the already existing bind method.
Please check back with my 2 most recent posts on this matter:
sandwich pattern in javascript code
Can you alter a Javascript function after declaring it?
... and how to implement it with the need of modifying the identification string "Function #1" automatically?
One does not need to since the console's time / timeEnd functionality only has to have
identical entry and exit points for measuring time (like the start/stop trigger of a stopwatch).
So one gets along with exactly the reference of the function/method one is currently running/measuring.
In order to solve the given task I will suggest around only instead of both before and
after for the former generates less overhead. The next code block exemplarily shows a
possible prototypal implementation. It also is the base for the afterwards following example
that finally might solve the OP's task.
(function (Function) {
var
isFunction = function (type) {
return (
(typeof type == "function")
&& (typeof type.call == "function")
&& (typeof type.apply == "function")
);
},
getSanitizedTarget = function (target) {
return ((target != null) && target) || null;
}
;
Function.prototype.around = function (handler, target) { // [around]
target = getSanitizedTarget(target);
var proceed = this;
return (isFunction(handler) && isFunction(proceed) && function () {
return handler.call(target, proceed, handler, arguments);
}) || proceed;
};
}(Function));
The next example takes into account that method-modification essentially relies on
functionality that is bound to an object. It is not just function wrapping. In order
to not loose the context a method is operating on, context has to be delegated /
passed around as target throughout all operations.
For this the example does not modify calculate since it is not bound to an object
but it modifies trigger instead.
var testObject = {
calculate: function (hugeInteger) {
var
i = hugeInteger,
k = 0
;
while (i--) {
k++;
}
return k;
},
trigger: function (hugeInteger) {
this.result = this.calculate(hugeInteger);
},
result: -1
};
console.log("testObject.result : ", testObject.result);
console.log("testObject.trigger(Math.pow(2, 26)) : ", testObject.trigger(Math.pow(2, 26))); // takes some time.
console.log("testObject.result : ", testObject.result);
console.log("testObject.someTrigger(0) : ", testObject.trigger(0)); // logs immediately after.
console.log("testObject.result : ", testObject.result);
testObject.trigger = testObject.trigger.around(function (proceed, interceptor, args) {
// before:
console.time(proceed);
// proceed:
proceed.apply(this, args);
// after:
console.timeEnd(proceed);
}, testObject); // omitting the 2nd argument - the [target] object - might break code that did work before.
console.log("testObject.trigger(Math.pow(2, 26)) : ", testObject.trigger(Math.pow(2, 26)));
console.log("testObject.result : ", testObject.result);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function (Function) {
var
isFunction = function (type) {
return (
(typeof type == "function")
&& (typeof type.call == "function")
&& (typeof type.apply == "function")
);
},
getSanitizedTarget = function (target) {
return ((target != null) && target) || null;
}
;
Function.prototype.around = function (handler, target) { // [around]
target = getSanitizedTarget(target);
var proceed = this;
return (isFunction(handler) && isFunction(proceed) && function () {
return handler.call(target, proceed, handler, arguments);
}) || proceed;
};
}(Function));
</script>
I am creating a variable where its value is determined by a particular method. This particular method should return an object with two properties. However, after the method returns the variable is undefined. I check the objects value right before it's returned and its fine. So there is something happening between the return and instantiate of the variable that causes it to be undefined. Here is a code snippet:
var results = findTarget(target, after, append); //undefined
function findTarget(target, after, append){
var currenttemplate = $(target).attr('data-template');
for(var i=0; i<after.length; i++){
if(after[i] === currenttemplate)
return {target : target, drop : "after"};
}
for(var j=0; j<append.length; j++){
if(append[j] === currenttemplate){
var obj = {target : target, drop : "append"};
console.log(obj); //is fine here
return obj; //this gets returned
}
}
if(currenttemplate === threshold) {
return "";
}
findTarget($(target).parent()[0], after, append);
}
You need to add a return at the end:
function findTarget(target, after, append){
/* ... */
return findTarget($(target).parent()[0], after, append);
}
If not, you call recursively findTarget, and this recursive call returns the appropriate value, but the first call to findTarget doesn't return it.
I am stuck at the following code. At first I'll describe the use-case: The function "addPreset" gets called with an instance of ColorGradient. When calling this.listController.addItem(...) a callback function named onSelect ist supplied, which gets called everytime the onSelect-event on the listController-item is triggered. What I wanted to do is wrapping the call to GLab.ColorSlider.applyColorGradient(...) into a new closure, so that the assigned value of addPreset's "cg" argument"* will be "caught" inside it. But it doesn't work.
PROBLEM: Now everytime addPreset is called, the value of cg (being passed with a call) will override all values that bad been assigned before. However, this.presetList holds always correct values (the ones I expected to be caught inside the closure-function. Even inserting an anonymous function for breaking the scope doesn't help.
Please help me. :-)
Thanks, so far
function addPreset(cg) {
if (!(cg instanceof ColorGradient)) {
throw new TypeError("PresetManager: Cannot add preset; invalid arguments received");
}
var newIndex = this.listController.addItem(cg.getName(), {
onSelect: (function(cg2) {
return function() {
// addPreset's scope should now be broken
GLab.ColorSlider.applyColorGradient(cg2);
console.log(cg2);
}
})(cg)
});
this.presetList[newIndex] = cg;
}
#bobince: of course you can.
the code snippet above is part of PresetManager.js and the listController is an instance of the class ListWrapper.js
http://code.assembla.com/kpg/subversion/nodes/GradientLab/lib-js/PresetManager.js
http://code.assembla.com/kpg/subversion/nodes/GradientLab/lib-js/ListWrapper.js
#Matt: cg is an instance of ColorGradient. A custom class of myself. Further more, it is assured, that always "valid" values are passed in as cg. (When you'd have a few minutes you can download the whole assembla repo as zip-archive. Unzip and test in FF > 3.5 with Firebug console enabled.)
Answer can be found in this question: Doesn't JavaScript support closures with local variables?
Someone please correct me if I am wrong, as I am still fairly new to JavaScript closures and scope. But it would seem to me that the wrapping anonymous function you have is simply there to provide a proper scoped variable/closure for the function it is returning. Could this be simplified as such?
function addPreset(cg) {
if (!(cg instanceof ColorGradient)) {
throw new TypeError("PresetManager: Cannot add preset; invalid arguments received");
}
var closured = cg;
var newIndex = this.listController.addItem(cg.getName(), {
onSelect: function() {
// addPreset's scope should now be broken
GLab.ColorSlider.applyColorGradient(closured);
console.log(closured);
}
});
this.presetList[newIndex] = cg;
}
Just want to tell you, that I finally solved my problem by myself. It cost me almost 2 days (in the sparetime) to puzzling it out, but I think its worth that. At least my code remained elegant and I definitely got the whole thing with closures. Let's have a look:
My faulty code
Part 1 of 2:
function addPreset(cg) {
if (!(cg instanceof ColorGradient)) {
throw new TypeError("PresetManager: blablabla");
}
// calls the function in Part 2
var newIndex = this.listController.addItem(cg.getName(), {
onSelect: (function(cg2) {
return function() {
// addPreset's scope should now be broken
GLab.ColorSlider.applyColorGradient(cg2);
console.log(cg2);
}
})(cg)
});
this.presetList[newIndex] = cg;
}
Part 2 of 2:
// The method being called by this.listController.addItem(..)
function addItem(caption, args) {
var _this = this,
currIndex,
id,
newItem
itemSelectCb = (!!args && typeof args.onSelect == "function") ?
args.onSelect :
undefined;
currIndex = this.numOfItems;
id = this.ITEM_ID_PREFIX + currIndex;
newItem = this.$itemTemplate
.clone()
.text(caption)
.attr("id", id)
.bind("click", function(e) {
e.stopPropagation();
if (typeof itemSelectCb != "undefined") {
itemSelectCb();
}
_this._onSelect($(".ListWrapperItem").index(this));
})
.appendTo(this.$container);
this.numOfItems = $("." + this.DEFAULT_ITEM_CLASS, this.$container).length;
return currIndex;
}
The fixed code
The bug was in Part 2; when calld jQuery's bind-method for adding an click-event-listener I used an anonymous function (= new closure), but referenced itemSelectCb inside; so the anonymous function's scope stayed "connected" to the one of addItem. Everytime I called addItem, an other value were assigned toitemSelectCb what lead to the unknown sideeffect, that all references to itemSelect inside previously created anonymous functions are pointing to that value. What meant, that the last assigned value, had been used by all anonymous function.
To "break" the scope, all I had to do was to modify the lines of Part 2 where the event-handler for jQuery's bind was created. The fixed code looks then like this:
function addItem(caption, args) {
var _this = this,
currIndex,
id,
newItem
itemSelectCb = (!!args && typeof args.onSelect == "function") ?
args.onSelect :
undefined;
currIndex = this.numOfItems;
id = this.ITEM_ID_PREFIX + currIndex;
newItem = this.$itemTemplate
.clone()
.text(caption)
.attr("id", id)
.bind("click", (function(itemSelectCb) {
return function(e) {
e.stopPropagation();
if (typeof itemSelectCb != "undefined") {
itemSelectCb();
}
_this._onSelect($(".ListWrapperItem").index(this));
}
})(itemSelectCb))
.appendTo(this.$container);
this.numOfItems = $("." + this.DEFAULT_ITEM_CLASS, this.$container).length;
return currIndex;
}