In my program, I have declared an object myObject like this :
function myObject()
{
this.start=start;
function start(callbackFunction)
{
// Do something with callbackFunction
}
}
In my main() method, I create objects and I want to start nested callback like this :
var myObject1 = new myObject();
var myObject2 = new myObject();
var list = [];
list.push(myObject1);
list.push(myObject2);
var result = function() {};
var obj;
for (var i=list.length-1; i>=0; i--) {
obj = list[i];
result = function() { obj.start(result);}
}
result(); // I want to do myObject1.start(myObject2.start)); e.g. a nested callback
I don't understand why it doesn't work.
How can I correct my code ?
The result variable is redefined after each iteration.
Your need to set your function like so :
var myObject1 = new myObject();
var myObject2 = new myObject();
var list = [];
list.push(myObject1);
list.push(myObject2);
var result= function() {};
for (var i=list.length-1; i>=0; i--) {
var obj = list[i];
result = obj.start.bind(obj, result);
}
result();
Using the bind method will force the state of the variable to be saved at each iteration.
The problem is that you are not using closures properly.
In your for loop you declare a function that uses a variable from the outer scope (result). When the for loop ends, the result variable will contain the last function defined, instead of the one defined at step i, as you would expect.
One solution as you so very well hinted in a comment is recursivity:
function myObject(name)
{
this.name = name;
this.start= function(callbackFunction) {
console.log(this.name);
// Do something with callbackFunction
callbackFunction();
};
}
var myObject1 = new myObject(1);
var myObject2 = new myObject(2);
var list = [];
list.push(myObject1);
list.push(myObject2);
var runner = function(list, currentIndex) { // recursive function
if (currentIndex < 0) return function(){ console.log('INITIAL');};
return function(){
list[currentIndex].start(runner(list, currentIndex-1));
};
};
runner(list, list.length-1)();
DEMO: http://jsbin.com/ukeBUweG/2/edit
One last note, the solution above tries to stay true to your initial code. It is not
obj1.start(obj2.start(function(){}))
, but
function(){ obj1.start(function(){ obj2.start(function(){}) })}();
Related
I have a bound function f:
var f = someFunction.bind(this);
Rather than passing f to a function, I save it in an object and instead pass an id to f:
var obj = {};
var nextID = Object.keys(obj).length;
obj[nextID] = f;
// pass nextID to some other function
However, I also want to delete entries at some point:
delete obj[someID]
This is a problem, because if I add some bound functions a and b and then delete a, the next id I get will be 1, and the bound function I put there will overwrite b.
What I could do is:
function getNextID() {
var possibleID = Object.keys(obj).length;
while (obj[possibleID])
possibleID *= 2; // or some other mutation
return possibleID;
}
But this feels ugly.
I tried using the bound functions as the key, but that didn't work:
obj[a] = a;
obj[b] = b;
console.log(Object.keys(obj).length); // 1
How can I get an arbitrary object property that isn't currently in use? Surely I'm overthinking this...
Wow, I should just use arrays.
var arr = [];
function f() { }
function f2() { }
var a = f.bind(this);
var b = f2.bind(this);
arr.push(a);
var id = arr.length; // 1
delete arr.id;
arr.push(b);
console.log(arr.length); // 2
I am trying to write a function that takes functions as arguments (as many as it gets) and returns them. The function funcArg should return 'Called me'. I used Array.prototype.slice.call(arguments); to create an array but I don't know how to call die functions in that array. Any ideas? Thanks!!
var caller = function() {
return "Called ";
};
var adder = function() {
return " me";
};
var funcArgs = function() {
var myArray = Array.prototype.slice.call(arguments);
}
funcArgs(caller);
funcArgs(calleradder);
You can do this using reduce.
var funcArgs = function() {
var functions = Array.prototype.slice.call(arguments);
return functions.reduce(function(total, f) {
return total + f();
}, '');
};
The way this works if you start off with an array of functions. We then go through each function one at a time. We then call that function and append it to the result of the previous function. Breaking this down into simpler code would look like this:
var funcArgs = function() {
var functions = [caller, adder];
var result = '';
result += functions[0](); // caller();
result += functions[1](); // adder();
return result;
};
If you have an array of functions you can loop over them with forEach.
var caller = function() {
return "Called "
}
var adder = function() {
return " me"
}
var funcArgs = function() {
var myArray = Array.prototype.slice.call(arguments);
myArray.forEach(function (fn) {
console.log(fn())
})
}
funcArgs(caller, adder); // "Called me"
If you want to actually return the values, rather than just console.log them, you can use reduce to return the strings concatenated (or whatever else)
var funcArgs = function() {
var myArray = Array.prototype.slice.call(arguments);
return myArray.reduce(function (acc, fn) {
return acc + fn()
}, '')
}
If a function returns more than one calculation and it's convenient to reuse that one function in few separate instances, is it best to store the result of the calculations in an internal array and just pull out of the array a calculation that's needed on that particular call or is there a more efficient way?
var calcFunction = function() {
var ar = [];
var calcA = ...
ar.push(calcA);
var calcB = ...
ar.push(calcA);
return ar;
}
If you're function is really long, it'll be much more efficient to use the array every time, however, this distance gets shorter and shorter as the function gets shorter. However, this isn't the only reason to use the array. Calling the function several times with the same values is essentially code duplication, which is a standard code smell.
Overall, if you can call a method as few times as possible, you've done your job right.
Here are options off the top of my head in order of my preference. Performance, if truly releavant, you should benchmark. Preference depends on your code and its purpose.
// If independent and appropriate, split it into two separate functions
// modularization into smaller reusable building blocks is good
var calcAFunction = function() {
var calcA = ...
return calcA;
}
var calcBFunction = function() {
var calcB = ...
return calcB;
}
// Returning an array
var calcFunction = function() {
var ar = [];
var calcA = ...
ar.push(calcA);
var calcB = ...
ar.push(calcA);
return ar;
}
// Returning an object, disadvantage you must maintain key names as opposed to index
var calcFunction = function() {
var o = {};
var calcA = ...
o.key1 = calcA;
var calcB = ...
o["key2"] = calcB;
return o;
}
// Calling it with a callback function
var calcFunction = function(cb) {
var o = {};
var calcA = ...
var calcB = ...
cb(calcA, calcB)
}
calcFunction(function(A, B) {})
UPDATE
// Returning an object, disadvantage you must maintain key names as opposed to index
var calcFunction = function() {
var o = {};
var calcA = ...
o.key1 = calcA;
var calcB = ...
o["key2"] = calcB;
return o;
}
var retObj = calcFunction();
//you can access retObj.key1 and retObj.key2
I'm trying to define a class that, in its constructor, instantiates other objects and passes them a reference to itself:
var Child = function(m) {
var mother = m;
return {
mother: mother
}
}
var Mother = function() {
var children = makeChildren();
return {
children: children
}
function makeChildren() {
var children = [];
for (var i = 0; i < 10; i++) {
var c = new Child(this); // <--- 'this' is an empty object here
children.push(c)
}
return children;
}
}
This doesn't work, and the Child instances end up with an empty object in their mother property. What is the proper way to do this?
Javascript's this is not lexical. This means that makeChildren gets its own this instead of getting the Mother's this you want.
Set a normal variable to this and use it instead.
var that = this;
function makeChildren(){
blabla = that;
}
I don't think doing this is just enough though. By returning an object from the constructor you ignore the this. Set things into it:
this.children = children;
instead of returning a new object.
You could try passing a reference to the mother object when you call makeChildren() from within the mother object, something like this maybe:
var Mother = function() {
var children = makeChildren(this);
}
The makeChildren() function can then accept as an argument the reference, which you can use:
function makeChildren(ref)
var c = new Child(ref);
No idea whether or not that will work, but it might be worth a try.
A nested function does not inherit this from its parent, so the this within makeChildren() is not the same as the this within the Mother constructor unless you explicitly set it when calling makeChildren():
var children = makeChildren.call(this);
That should work without any further changes to your code. Have a look at MDN for more detail about .call().
Alternatively you can save a reference to this and pass that into the function:
var Mother = function() {
var self = this; // <-- new variable
var children = makeChildren();
return {
children: children
}
function makeChildren() {
var children = [];
for (var i = 0; i < 10; i++) {
var c = new Child(self); // <--- change 'this' to 'self'
children.push(c)
}
return children;
}
}
Local variables within a function are accessible to nested functions.
var Child = function(m) {
var mother = m;
return {
mother: mother
}
};
var Mother = function() {
if (!(this instanceof Mother)) {
return new Mother();
}
var that = this;
var makeChildren = function() {
var children = [];
for (var i = 0; i < 10; i++) {
var c = new Child(that); // <--- 'that' is the reference to Mother
children.push(c)
}
return children;
};
var children = makeChildren();
return {
children: children
}
};
then do:
var m = Mother();
The first three lines in the Mother object ensure that the value of that is the Mother instance and not the global object. Without them, you would always have to write:
var m = new Mother();
Say i have this function that dynamically creates my namespace for me when I just pass it a string, (I'm pretty sure basically what YUI JS library does):
MyObj.namespace('fn.method.name');
would result in
MyObj.fn.method.name = {}
being created - all three levels being equivalent to an empty object.
Now, what I want to do, though, is make the last level, in this case name, set to a function, but without having to redeclare the newly created object.
So instead of doing this:
function fnName() { /* some code here */ }
MyObj.namespace('fn.method.name');
MyObj.fn.method.name = new fnName();
i want to call something like:
MyObj.add('fn.method.name', fnName);
And internally, the add method would programmatically instantiate the passed in function:
MyObj.fn.method.name = new fnName()
In the way I have it implemented, I can create the namespace object and set it to an empty object, however, when I try to instantiate a passed in function and associate that namespace with the passed in function, it never gets added to the namespace. Instead, an empty object is always returned. Any ideas?
edit: Here is the namespace method. this is attached to the base object as a JSON object, so please ignore the formatting:
namespace: function (ns) {
var _ns = ns.split('.'),
i = 0, nsLen = _ns.length,
root = this;
if (_ns[0] === gNS) {
_ns.shift();
nsLen = _ns.length;
}
for (i = 0; i < nsLen; i++) {
// create a property if it doesn't exist
var newNs = _ns[i];
if (typeof root[newNs] === "undefined") {
root[newNs] = {};
}
root = root[newNs];
}
return root;
}
edit2 - removed the passed in fn argument
Were you looking for something like this:
var root = {};
function create(ns, fn) {
var nsArray = ns.split(/\./);
var currentNode = root;
while(nsArray.length > 1) {
var newNS = nsArray.shift();
if(typeof currentNode[newNS] === "undefined") {
currentNode[newNS] = {};
}
currentNode = currentNode[newNS];
}
if(fn) {
currentNode[nsArray.shift()] = fn;
}
else {
currentNode[nsArray.shift()] = {};
}
}
Then:
create("a.b.c");
console.log(root.a);
console.log(root.a.b);
console.log(root.a.b.c);
Gives:
Object { b={...}}
Object { c={...}}
Object {}
And:
create("d.e.f", function() { console.log("o hai"); });
console.log(root.d);
console.log(root.d.e);
console.log(root.d.e.f);
Gives:
Object { e={...}}
Object {}
function()
Calling the function you defined:
root.d.e.f();
Gives:
o hai
Well you haven't given the namespace function but your add function could look something like this:
MyObj.add = function (namespace, value) {
var names = namespace.split('.'), current = this, name;
while (names.length > 1) {
name = names.shift();
current[name] = {};
current = current[name];
}
current[names[0]] = value;
};
This code assigns the value given to the last part of the namespace. You could modify it to current[names[0] = new value(); if you want the object constructed by the passed in function (and you are assuming the constructor function takes no arguments).
function ns() {
var root = window;
for (var i = 0; i < arguments.length; i++) {
var arr = arguments[i].split(/\./);
for (var j = 0; j < arr.length; j++) {
var item = arr[j];
if (typeof item !== 'string') {
root = item;
}
else {
if (!root[item]) {
root[item] = {};
}
root = root[item];
}
}
root = window;
}
}
then you can create using
ns('fn.method.name');
or
ns('fn.method.name','fn.method.secondName');
and call using
fn.method.name
this function creates your namespace on 'window' so alternatively you can use
window.fn.method.name