After reading over it several times, I still don't understand how this example code from page 76 of Stoyan Stefanov's "JavaScript Patterns" works. I'm not a ninja yet. But to me, it reads like it's only storing an empty object:
var myFunc = function (param) {
if (!myFunc.cache[param]) {
var result = {};
// ... expensive operation ...
myFunc.cache[param] = result;
}
return myFunc.cache[param];
};
// cache storage
myFunc.cache = {};
Unless that unseen "expensive operation" is storing back to result, I don't see anything being retained.
Where are the results being stored?
P.S.: I've read Caching the return results of a function from John Resig's Learning Advanced JavaScript, which is a similar exercise, and I get that one. But the code is different here.
You've answered your own question -- the author assumes that the expensive operation will store its result in result.
The cache would otherwise only contain empty objects, as you've noted.
the results are being stored in the object literal called 'cache'. What the code is specifically doing is:
when myFunc gets executed with a param, the function first checks the cache. If there is a value for 'param' in the cache, it returns it. If not, you do the expensive operation, and then cache the result(with param as the key), so the next time the function is called with the same param the cache is used.
It says // expensive operation - the inference is that you implement code there which assigns variables into the result var, or sets the result var to another Object (which is the result of an expensive operation)
Related
I have a recursive function:
const fn = async (val) => {
let obj = await make_some_api_call();
// example response {a: 'apple', b : 'bbb', c: 'ccc', d: 'ddd'};
if ('a' in obj) {
const var1 = obj.a;
obj = null;
return fn(var1);
}
}
I want the Object obj to be gc'ed after each run.
I have a Object property value assigned to a local variable (var1), will setting obj=null force it to be gc'ed in next cycle?
Meaning after each run will Object obj get gc'ed?
If not, how to achieve this?
Since some commentors have missed the fact that this is potentially recursive code, I want to point out that this answer is written in that recursive context. If it wasn't recursive, then setting obj = null would not be necessary because the variable obj would immediately be eligible for garabage collection anyway as soon as the function returned.
will setting obj=null force it to be gc'ed in next cycle?
Assuming that no other code such as code inside of await make_some_api_call(); has any persistent references to obj, then setting obj = null will clear your one reference to that variable and will make it "eligible" for garbage collection at the point in time where nodejs next runs a garbage collection cycle.
That may or may not be "after each run". You don't really describe what "after each run" means as it pertains to your code, but in any case, when the garbage collector runs is not precisely defined.
Nodejs will run a GC cycle when it thinks it needs to and when it appears to have time to. Garbage collection can sometimes be a bit lazy. If you're busy doing a lot of things, then nodejs will attempt to not get in the way of what your code is doing just to run the GC. It will try to wait until the event loop isn't really doing anything. There are exceptions to this, but to avoid impacting run-time performance, it looks for opportunities to run GC when the event loop is idle.
In your recursive code, you do have an await so assuming that takes some amount of time to resolve its promise, then that next await could be an opportunity for nodejs to run the GC cycle to clean up the obj from the prior recursive call.
I should also point out that code like this can also be written with some sort of loop instead of using recursion and that sometimes simplifies things. For one, it prevents stack-build up. Since there is no complicated local function context or lots of function arguments, this could easily be turned into a while(more) kind of loop with the await and some sort of condition test inside the loop that either sets the more flag or uses break or return to stop the looping when done. If this may recurse many times, just avoiding the stack build-up (which also includes a promise for each async function called recursively) could be beneficial.
Here's some similar pseudo-code that avoids the recursion and automatically reuses the obj variable (freeing the reference to the prior object to it is available for GC):
const fn = async (val) => {
// value for the first one comes from the function argument,
// subsequent iterations get the value from the prior call
let var1 = val;
let obj;
while (true) {
obj = await make_some_api_call(var1);
if (!('a' in obj)) {
// all done
break;
}
// run again using obj.a
var1 = obj.a;
}
}
I would like to set a localStorage item as a function, but when I do it gets saved as a string.
localStorage.getData = function (key="def") {
let data = null;
if (this[key] && !_this.isDataAvailableInList(this[key])) {
data = this[key].data;
}
return data;
};
The above function get stored as string and not function.
Note: Kindly do not provide alternatives or other suggestions, I am well aware of alternatives and am curious to know how this works. I have not provided the entire context so lets just assume I am in need of setting a localStorage item as a function
Unfortunately local storage is only about strings. Even arrays or other data are always converted to strings.
For most dead data without cycles you can use of course JSON.stringify and JSON.parse, but in other cases or for code or other Javascript entities unsupported by JSON (like Infinity or undefined) you need to serialize and deserialize yourself.
Javascript provides eval, so you can store the function f converting it first to a string with f+"" and get back the function with eval(s).
This won't work with closures however and moreover you cannot even know if a function is indeed a closure or not in Javascript (so you cannot even raise an error if the impossible operation of serializing a closure is attempted).
Convert the function into a string via .toString(). When you read it, convert it back to a function via new Function(myString).
Example:
No pamameters:
(new Function('var a = 0; return a + 1;'))() === 1
With parameters:
(new Function(['param0'],'var a = param0; return a + 1;'))(100) === 101
Note: The scope of the original function will be lost.
I have a function:
// Get the value out of Chrome local storage
chrome.storage.local.get(sourcePath, function(result) {
// Test the result
alert(JSON.stringify(result.sourcePath));
});
And a call to check it:
chrome.storage.local.set({'userAcceptanceAgreement': true});
Right now, I would like to know how to get my function, when called with the above argument, to pass that value into the result.sourcePath alert. As is, it shows result with the correct storage value of {'userAcceptanceAgreement': true}, but result.sourcePath comes up as undefined because it isn't trying to locate the key:value pair for the argument, but for a literal sourcePath.
Basic JavaScript here.
var sourcePath = 'userAcceptanceAgreement';
result.sourcePath; // Accesses literally "sourcePath"
result[sourcePath]; // Accesses "userAcceptanceAgreement"
Note that you buried the problem under a lot of noise. Please consider constructing a minimal example next time.
Is there any kind of persistence framework for JavaScript and/or the Google v8 engine?
I want to store (serialize) a whole graph of objects (including, e.g., functions) and re-load it later. JSON is not sufficient, since it does not permit functions to be stored and permits only a tree-like structure (i.e. no two objects referencing the same object).
I need to be able to do that generically (i.e. without knowing the JavaScript code at the time at which I write my program embedding v8), since I want the user of my program to be able to customize it with JavaScript, but I need to store the state of my program (including the state of the customization) and re-load it later. Hence I need to store the state of the JavaScript engine.
Edit:
Example:
Suppose we have the following code:
var obj = { a: 4, b: function (x) { return x + this.a; } }
// ...
if ( ... ) { obj.a = 5; }
// ...
if ( ... ) { var c = 1; obj.b = function (x) { return x + this.a + c; } }
// ...
// now I want to serialize obj
Then is it (without any meta-information about the logic of the program) possible to serialize obj and later deserialize it such that obj.b (2) delivers the same result after deserialization as it did before serialization?
Second Edit: Note the closure.
Unfortunately, what you're trying to do is not currently possible in Javascript. The reason is that closures are not just objects, they're objects bound to an execution context.
Getting past the "this can't be done in javascript" issue and moving into the "what if wrote a patch for V8 to allow this" phase of the answer, this is conceptually difficult. Essentially, for every closure you'd serialize, you would have to serialize the Context object that the closure exists in. It'd be nice to be able to just serialize the HandleScope, but the nature of closures is that you can't reach inside them.
Okay, so let's say you've written a function that can serialize the Context that the closure exists in, and you can even deserialize it. What do you do with it?
The answer to that is 'not much'. Javascript can only be executed in a single context at a time. The closure that you've deserialized doesn't exists in the context that you're trying to pull it back into. You can't really pass data between contexts, and if your function has data bound to free variables, do you use the ones that exist in the deserializer-invoking context, or do you overwrite it with the deserialized context? Conceptually, this is a nightmare.
Ecmascript Harmony had considered giving us nearly-first-class continuations, but it's been pushed form the discussion which I rant about here, but this isn't going to happen any time soon.
HTML5 local storage allows persistence at client level through javascript.
I'm not sure if it will fit your needings, as to being able to store a function you'll need to somewhat give it some markup that allows you to deserialize it when retrieving it from storage (or maybe just store it as plain text and try to eval it on retrieval)
http://diveintohtml5.info/storage.html
I don't think persisting functions is a good practice. I can suggest you the below approach. Turn your JSON data to lets say some class like "MyData". You can find two functions fromJSON, toJSON which will do the magic you want.
var MyData = function(props){
this.temp = "a";
this.getTemp = function(){
return this.temp;
}
this.fromJSON = function(props){
if(props){
this.temp = props.temp;
}
}
this.toJSON = function(){
var props = {};
props.temp = this.temp;
return props;
}
this.fromJSON(props);
}
var obj = new MyData({"temp" : "b"});
var state = obj.toJSON();
// persist state about the object as JSON string
LOCALSTORAGE.put(state); // You can write some HTML5 local storage stuff to persist
var persistedState = LOCALSTORAGE.get(); // You can use the above HTML5 local storage stuff to read the persisted stuff
var newBornObj = new MyData(persistedState);
Sorry if this has been answered already, but I could not find an appropriate answer on here.
I've started writing my javascript code in a modular style lately and I have a question regarding how module variable scope works.
The following code gives me a conflicting answer.
I have a module named Base that declares two strings and an array. It also has a function called fetchData that uses the jQuery getJSON shortcut to set these variables with server data. Unfortunately when I ask for Base's string1 or string2, I get undefined. I understand that this is probably due to the fact that I have it set their values two functions deep (inside the AJAX callback and inside fetchData) and the scope limits it from seeing Base.string1 and Base.string2.
However, when I look at Base.array1 from outside the module, it's set to the appropriate data I pulled from the server, even though it's set from the same scope as the strings.
Here's the code:
namespace.Base = (function(){
var string1, string2, array1 = [];
function fetchData(){
$.getJSON('backendScript.php', function(data){
string1 = data.string1;
string2 = data.string2;
arrayCount = data.arr.length;
for(var i = 0; i<arrayCount; i++){
array1[i] = data.arr[i];
}
})
}
return{
fetchData: fetchData,
string1: string1,
string2: string2,
array1: array1
}
})();
If I change
string1 = data.string1;
to
namespace.Base.string1 = data.string1;
it works like I want.
So my question is, why is array1 set correctly when it's set from the same scope as the strings?
Also, what is the remedy for setting module-level variables from within the module's functions without having to give a global path (e.g. namespace.Base.string1)?
The problem is that you actually have two different references, the variable string1 within the closure of the anonymous function that you invoke to create namespace.Base, and namespace.Base.string1, which is on the object returned from that anonymous function. Your assignment of the variable string1 to the object property string1 is a one-time set, not a live reference. Further modification of the variable string1 will not affect the object property. Here's what you want:
namespace.Base = (function() {
var my = {
string1: null,
string2: null,
array1: [],
fetchData: function () {
$.getJSON('backendScript.php', function(data){
my.string1 = data.string1;
my.string2 = data.string2;
var arrayCount = data.arr.length;
for (var i = 0; i < arrayCount; i++){
my.array1[i] = data.arr[i];
}
});
}
};
return my;
})();
Now the local, but public, members of namespace.Base are in the object my. You can create private variables using var within the anonymous function, or create more public properties by adding them to my.
I would be a good idea to get familiar with closures and how they work:
How do JavaScript closures work?
Your "scope" issue is not actually a scope issue. The issue is that arrays are pointers to their data, strings are not.
namespace.Base is set to the results (returned value) of the anonymous function. -- It is set to be an object containing a function ref (fetchData), two empty strings and an array.
If you later call the fetchData function, then it will change the contents of array1.
But it will also create two new strings (from data.string1 and data.string2). The old values of string1 and string2 (which are namespace.Base.string1 and namespace.Base.string2) are not changed. So they are left as empty strings (not what you want).
Example of this. Try it in Firebug--
s1 = "Hi";
s2 = s1; // s2 => "Hi"
s1 = "Bye"
alert(s2); // *** s2 is still "Hi", it was not changed!
// But arrays are different:
a1 = ["Hi"];
a2 = a1;
a1[0] = "Bye";
alert(a2[0]); // a2[0] is now "Bye"
Added: Asynch Timing error
Also, note that your code is wrong as written since you're not giving the caller any way to know when the Ajax call has completed:
namespace.Base.fetchData(); // starts the Ajax call via getJSON method
var a = namespace.Base.array1; // ERROR!! The value of namespace.Base.array1 is
// indeterminate since you don't know if the
// the Ajax request has completed yet or not!
You appear to be trying to convert the asynchronous Ajax call (which invokes a callback function once the answer has been received from the remote server) into a synchronous call which will not return until the results have been received.
This is a really bad idea. (If you want to know more, ask another question in SO.)