Javascript memoization without closure scope - javascript

In Secrets of the JavaScript Ninja the author's propose the below scheme for memoizing function results without a closure. They do this by exploiting the fact that functions are objects and defining a property on the function that stores the results of past calls to the function.
function isPrime(value) {
if (!isPrime.answers) isPrime.answers = {};
if (isPrime.answers[value] != null) {
return isPrime.answers[value];
}
var prime = value != 1
for (var i = 2; i < value; i++) {
if (value % i === 0) {
prime = false;
break;
}
}
return isPrime.answers[value] = prime;
}
I have two questions
Their logic makes sense to me, but when I run the code all that happens is the answers property is created but nothing gets added to it - why?
The line return isPrime.answers[value] = prime; is funny to me, and repl.it warns when I use it. Is assigning and returning all in the same line frowned upon?

1. Their logic makes sense to me, but when I run the code all that happens is the answers property is created but nothing gets added to it - why?
It seems to work just fine for me.
console.log(isPrime(42));
console.log(isPrime.answers)
shows that answers is a non-empty object:
function isPrime(value) {
if (!isPrime.answers) isPrime.answers = {};
if (isPrime.answers[value] != null) {
return isPrime.answers[value];
}
var prime = value != 1
for (var i = 2; i < value; i++) {
if (value % i === 0) {
prime = false;
break;
}
}
return isPrime.answers[value] = prime;
}
console.log(isPrime(42));
console.log(isPrime.answers)
2. [...] Is assigning and returning all in the same line frowned upon?
In this situation, the assignment is basically a side effect, and side effects are frowned upon. That said, assignments being expressions and returning the assigned value is a feature of the language and as long as it's used responsibly, why not.

Their logic makes sense to me
It should not. The scheme still relies on closure, in particular over the isPrime variable in the scope that the function resides in. They could equally have used var isPrimeAnswers = {}; instead of isPrime.answers = {}; (both of which should be put outside of the function body).
Is assigning and returning all in the same line frowned upon?
Depends on whom you ask, but it's not unanimously condemned as a bad practise. You say it's funny, others find it confusing, I personally think it's crystal clear. If you're not into code golf and want to be on the safe side, it's better to simply split it in two statements:
isPrime.answers[value] = prime;
return prime;

Related

transpiler battle: breaking out of nested function, with vs without throw

I have just finished writing "version 0" of my first (toy) transpiler. It works. It turns a string of "pseudo JavaScript" (JavaScript with an additional feature) into a string of runnable JavaScript. Now, I want to improve it.
The work area possibly most interesting for other SO users is this: The compiled code (i.e., output of my transpiler) does not heed a coding style recommendation as given in an accepted answer to some earlier SO question. If I would have at my hands a second transpiler where that coding style recommendation is heeded, I could make an informed decision regarding which branch is more promising to continue to develop on - I'd like to compare the 2 braches regarding performance, development time needed, amount of bugs, and so on, and decide based on that.
Let me tell you about the "additional JS feature" my transpiler deals with: "nested return". Consider closures / nested functions like so
function myOuterFunc(){
... code ...
function innerFunc(){
... code ...
}
... code ...
}
(note that above '...code...' is supposed to include every possible JS code including more nested function declarations, so myOuterFunc is not necessarily the direct parent of innerFunc)
In above situation, suppose you desire to return a result from myOuterFunc from somewhere inside - not necessarily directly inside - innerFunc
With "nested return" implemented, you could then write simply
return.myOuterFunc result
Here is an exmalpe of a (not-runnable) function using this feature and doing something meaningful
function multiDimensionalFind(isNeedle, haystack) {
// haystack is an array of arrays
// loop (recursively) through all ways of picking one element from each array in haystack
// feed the picked elements as array to isNeedle and return immediately when isNeedle gives true
// with those picked elements being the result, i.e. the 'found needle'
var LEVEL = haystack.length;
function inner(stack) {
var level = stack.length;
if (level >= LEVEL) {
if (isNeedle(stack)) return.multiDimensionalFind stack;
} else {
var arr = haystack[level];
for (var i = 0; i < arr.length; i++) {
inner(stack.concat([arr[i]]));
}
}
}
inner([]);
return 'not found'
}
And here is the (runnable) code my transpiler automatically produces from that (comments were added/remove later, obviously), followed by some code testing if that function does what it claims to do (and it does, as you can convince yourself.)
///////////// the function /////////////////
function multiDimensionalFind(isNeedle, haystack) {
try {
var LEVEL = haystack.length;
function inner(stack) {
var level = stack.length;
if (level >= LEVEL) {
if (isNeedle(stack)) throw stack;
} else {
var arr = haystack[level];
for (var i = 0; i < arr.length; i++) {
inner(stack.concat([arr[i]]));
}
}
}
inner([]);
return 'not found'
} catch(e){
// make sure "genuine" errors don't get destroyed or mishandled
if (e instanceof Error) throw e; else return e;
}
}
////////////////// test it //////////////////
content = document.getElementById('content');
function log2console(){
var digits = [0,1];
var haystack = [digits,digits,digits,digits,digits];
var str = '';
function isNeedle(stack){
str = str + ', ' + stack.join('')
return false;
}
multiDimensionalFind(isNeedle, haystack);
content.textContent = str;
}
function find71529(){ // second button
var digits = [0,1,2,3,4,5,6,7,8,9]
var haystack = [digits,digits,digits,digits,digits]
function isNeedle(stack){
return stack.reduce(function(b,i){ return 10*b+i; }, 0) === 71529
// returns true iff the stack contains [7,1,5,2,9]
}
content.textContent = multiDimensionalFind(
isNeedle, haystack
).join('_')
}
<button onclick='log2console()'>print binary numbers with 5 digits</button>
<br>
<button onclick='find71529()'>find something is 5d space</button>
<div id='content'></div>
You can play around with my transpiler in this fiddle here. I'm using the esprima library, the escodegen.js library on top of esprima, a teeny tiny work in progress abstract syntax tree generation library of my own (see the script tags in the fiddle). The code which is not library, and not UI code, i.e. the "real meat" of the transpiler has less than 100 lines (see function transpile). So this might be a lot less complicated than you thought.
I can't remember where I had seen the style recommendation, but I am certain that it was actually in multiple places. If you know or come across one such question, I invite you to be so kind to put the link into a comment under the question, I will mark useful. So far, there is one link, thank you Barmar.
You might ask why I even bothered to write a "non-compliant" transpiler first, and did not go for the "compliant" version straight away. That has to do with the estimated amount of work. I estimate the amount of work to be much larger for the "compliant" version. So much so, that it didn't really seem worthwhile to embark on such an endeavor. I would very much like to know whether this assessment of the amount of work is correct or incorrect. Thus the question. Please do not insinuate a rhetorical, or even a dishonest motive for the question; however weird it might sound to some, it really is the case that I'd like to be proven wrong, so please be so kind not to assume I'm "just saying that" for whatever reason, you'd be doing me an injustice. This is, of all the questions I asked on SO so far, by far the one I've put the most work into. And, if you ask me, it is by far the best question I have ever asked here.
Apart from someone assisting me in writing the "compliant" version of the transpiler, I'm also interested (albeit to a lesser degree) in anything objectively demonstrable which stands a chance to convince me that the "non-compliant" way is the wrong way. Speed tests (with links to jsperf), reproducible bug reports, things of that sort.
I should mention the speed tests I have done myself so far:
first test, second test
loosely related question
The better way really is to use return (if you can't completely refactor the tower). It makes it clear what you're doing and when you're returning a value from the intermediate functions; you can tell by looking at those functions where the result may be provided. In contrast, using throw is invisible in those intermediate layers; it magically bypassing them in a way designed for error conditions.
If you don't want to do that, I don't think you have a reasonable alternative other than throw. I wondered about going down the route of generator functions or similar, but I think that would just complicate things more, not less.
Using return doesn't markedly complicate the example code, and does (to my eye) make it clearer what's going on and when results are potentially expected.
function wrapper(){
function first(){
function second(){
function third(){
doStuff4();
some loop {
var result = ...
if (something) return result;
}
}
doStuff2();
let result = third();
if (result) {
return result;
}
doStuff3();
return third();
}
doStuff1();
return second();
}
return first() || "not found";
}
(In the above, result is tested for truthiness; substitute something else if appropriate.)
Ok, here is another approach with use of all async power of JavaScript... So basically I've recreated your nested functions but with use of Promise/await technique. You will get result only once, the first time you'll resolve it from any level of nested functions. Everything else will be GC. Check it out:
// Create async function
(async () => {
const fnStack = (val) => {
return new Promise((resolve, reject) => {
// Worker functions.
// Could be async!
const doStuff1 = (val) => val + 1;
const doStuff2 = (val) => val * 2;
const doStuff3 = (val) => val * -1; // This will not affect result
const doStuff4 = (val) => val + 1000;
// Nested hell
function first() {
function second() {
function third() {
val = doStuff4(val);
// Some loop
for(let i = 0; i < 1000; i++) {
if(i === 500) {
// Here we got our result
// Resolve it!
resolve(val);
}
}
}
val = doStuff2(val);
third();
// Below code will not affect
// resolved result in third() above
val = doStuff3(val);
third();
}
val = doStuff1(val);
second();
}
//
first();
});
}
// Run and get value
const val = await fnStack(5);
// We get our result ones
console.log(val);
})();
I think you should use arrays;
const funcs = [first, second, third];
for(let i = 0; i < funcs.length; ++i){
const result = funcs[i]();
if (result) break;
}
You should return only from function that has the result.
Sometime later, when I get around to it, I'll add instructions here on how to use my abstract syntax tree generation library which I used for my transpiler, maybe even start a little towards the other version, maybe explaining in more detail what the things are which make me think that it is more work.
old version follows (will be deleted soon)
If the feature is used multiple times we'll need something like this:
function NestedThrowee(funcName, value){
this.funcName = funcName;
this.value = value;
}
return.someFunctionName someReturnValue (before compilation) will give (after compliation) something like
var toBeThrown = new NestedThrowee("someFunctionName", someReturnValue);
And inside the generated catch block
if (e instanceof Error){
throw e; // re-throw "genuine" Error
} else {
if (e instance of NestedThrowee){
if (e.funcName === ... the name of the function to which
this catch block here belongs ...) return e.value;
throw new Error('something happened which mathheadinclouds deemed impossible');
}
In the general case, it is necessary to wrap the result (or 'throwee') like shown above, because there could be multiple, possibly nested "nested returns", and we have to take care that the catch phrase and caught object of type NestedThrowee match (by function name).

How can I return a value from function with multiple nested functions in Javascript?

The problem: I want to call a value from a nested method outside of its parent method. In other words, I want the output from "console.log(someObjects[i].valueChecker);" to be either "true" or "false." However, it is just returning the function itself.
What I've done so far: So I have been scouring the web/stack overflow for a solution, but either I haven't found a solution, or I just can't make sense of it. I think it has something to do with "closures," and most of the solutions to problems I've seen have been to return from the submethod, and then return the submethod from the parent method. However, every time I've tried this, I've just encountered numerous errors-- either another submethod suddenly doesn't exist, or the code runs, but the output is still a function. I wonder if having multiple methods affects the issue.
Context: I'm making a platformer game, and there are multiple types of the same enemy. I want to check for collision between the player and weapon and thusly need some values from the enemy function (I don't want to use the word "class," but I'm not sure about the appropriate terminology). I'm much more familiar with Java though, so it is frustrating me to not be able to create a separate class and just have a method to give me values back.
//assume all the other html/main stuff is already set up
var temp = {
create: c4,
update: u4
}
MyObject = function(value) {
this.value = value; //passed in value
var magicNumber = 4; //local value initialized/declared
this.valueChecker = function() {
//return boolean
return this.value == this.magicNumber;
}
this.otherValueChecker = function() {
//return boolean
return (this.value + 1) == this.magicNumber;
}
}
//just make the space bar tied to a boolean
var someKeyPress;
function c4() {
someKeyPress = game.input.keyboard.addKey(Phaser.Keyboard.A);
}
var someObjects = [];
//... later on in the program, presuming key already coded
function u4() {
//add a new MyObject to array someObjects
if (someKeyPress.isDown) {
//check with various random numbers between 3 to 5
someObjects.push(new MyObject(game.rnd.integerInRange(3, 5)));
}
//run through MyObject(s) added to someObjects, and see if any match number
for (var i = 0; i < someObjects.length; i++) {
console.log(someObjects[i].valueChecker);
}
}
/* current output
ƒ () {
//return boolean
return this.value == this.magicNumber;
}
*/
Try
console.log(someObjects[i].valueChecker())
Because I see the value checker as a function
this.valueChecker = function()

Difficulty in understanding javascript coding

i'm facing difficulty in understanding what this following code does. could anyone here please help me out in understanding this piece of code?
var PnPResponsiveApp = PnPResponsiveApp || {};
PnPResponsiveApp.responsivizeSettings = function () {
// return if no longer on Settings page
if (window.location.href.indexOf('/settings.aspx') < 0) return;
// find the Settings root element, or wait if not available yet
var settingsRoot = $(".ms-siteSettings-root");
if (!settingsRoot.length) {
setTimeout(PnPResponsiveApp.responsivizeSettings, 100);
return;
}
}
var PnPResponsiveApp = PnPResponsiveApp || {};
The above line ensures that the PnPResponsiveApp variable gets its old value if it already exists, otherwise it's set to a new object.
PnPResponsiveApp.responsivizeSettings = function () {
Here, a new function is created.
// return if no longer on Settings page
if (window.location.href.indexOf('/settings.aspx') < 0) return;
If the URL of the current page isn't the settings page, then the function exits immediately.
// find the Settings root element, or wait if not available yet
var settingsRoot = $(".ms-siteSettings-root");
This gets all elements with a class of .ms-siteSettings-root.
if (!settingsRoot.length) {
setTimeout(PnPResponsiveApp.responsivizeSettings, 100);
return;
}
If any elements were found (if the length of the node list is not zero), then call the PnPResponsiveApp.responsivizeSettings function in 100 milliseconds.
Very easy code basically, I'll explain what's going on:
var PnPResponsiveApp = PnPResponsiveApp || {};
This is very common way to see if the variable is already defined and if not, avoid throwing error and equal it to an empty object, It's used in many frameworks and library, very safe way to check if the var is there already... look at here for more info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators
PnPResponsiveApp.responsivizeSettings = function () {};
This is basically a simple function but attached to the object PnPResponsiveApp - if just responsivizeSettings = function () {}; it's attached to window Object
if (window.location.href.indexOf('/settings.aspx') < 0) return;
this is Checking if the Link in the linkbar has settings.aspx - indexOf return -1 if it doesn't contain the string, so if it's not settings.aspx it returns -1 that's smaller than 0 and then the whole function return ... the second return basically return undefined
var settingsRoot = $(".ms-siteSettings-root");
This is basically look for all element with class of ms-siteSettings-root and equal them to variable settingsRoot, it could be a single DOM or multiple...
if (!settingsRoot.length) {
and this basically check if any DOM element has ms-siteSettings-root class, length return a Number, so if it's not there, it returns 0, if there is return 1,2,3 etc... and 0 is equal to False in JavaScript and bigger than 0 is equal to True, so this way we can check if it's there...
setTimeout(PnPResponsiveApp.responsivizeSettings, 100);
so if the settingsRoot is there, we execute this function block and with setTimeout we wait 100ms... setTimeout always works in this manner, setTimeout(function(), time); and the same return happens at the end...
Hope it's informative enough...

Do I need a closure inside a DOM event callback?

I'm trying to build a jQuery.live like function.
Helper is a class that has the _liveEvent and _addEventListener methods. Helper._addEventListener is just a CrossBrowser version of W3C addEventListener.
Helper.prototype._liveEvent = function(type, evt, ofunc) {
var elHand = document;
type = type.toUpperCase();
this._addEventListener(elHand, evt, function(me) {
// Inside here I use the `type` variable.
// I don't know why but it works.
for (var el = me.srcElement; el.nodeName !== 'HTML';
el = el.parentNode)
{
if (el.nodeName === type || el.parentNode === null) {
break;
}
}
if (el && el.nodeName === type) {
ofunc.call(el, me);
}
});
};
I'm running the Helper._liveEvent function twice with different types, and it works just fine. I thought that since the type variable was set inside the _liveEvent context the _addEventListener callback could see only the last version of that variable. But it's not the case, it seems to be working fine.
My questions are:
Why the _addEventListener callback can see both versions of the type?
Does it mean my code is leaking memory?
UPDATE
This other example made me understand this better, but I'm not sure I understand it fully yet.
function foo(i) {
setTimeout(function() {
console.log(i);
}, 400);
}
// Prints 1, 2, 3
for (var i = 1; i < 4; i++) {
foo(i);
}
function bar() {
for (var i = 1; i < 4; i++) {
setTimeout(function() {
console.log(i);
}, 400);
}
}
// Prints 4, 4, 4
bar();
​
It's because a separate closure scope is created for every instance of the anonymous function passed to _addEventListener(), each having its own values of elHand and type.
It depends on what you mean by "leaking". Every closure prevents objects that it contains from GC'ing. A closure is GC'ed when there are no more objects (say, anonymous functions like yours) referencing it. In this sense, yes, you have a memory leak, as you have no way to remove the added listener (anonymous function) thereby making the associated scope object eligible for GC.
Effectively, you already are creating a closure. This is why:
for( var i=0; i<10; i++) {
elem.onclick = (function(id) {alert(id);})(i);
}
works - calling the anonymous function creates a new closure with id set to the current value of i. (Personally I like to call the argument the same thing as the variable I want to use, so I can think of it as "locking" the value of the variable for that function).
As far as memory leaks go, two calls is not going to cause a leak. If GC works the way I think it does, it removes any closures that have no pointers to them. In particular, when you leave a page, any memory associated with that page is freed.

Cannot call method 'split' of undefined - Called From Function

I have a JS function that is called on load that spilts some variables, this all works well, but when I call the function from another function, I get this error Cannot call method 'split' of undefined:
function loadInAttachmentsIntoSquads(){
// eg: 5000,5000,5000,5000 > [5000][5000][5000]
myAttachmentArray = currentAttachments.split(',');
//eg: [5000][5000][5000] > [5][0][0][0]
//myAttachmentForWeapon = myAttachmentArray[mySquadsIndex].split('');
setupWeaponAttachments();
}
function setupWeaponAttachments(){
myAttachmentForWeapon = myAttachmentArray[mySquadsIndex].split('');
//if(mySquadsIndex == 0){
if(myAttachmentForWeapon[1] == 1){ // if silencer is on? //first digit is always 5
weaponAttachments.silencer = true;
}
else{
weaponAttachments.silencer = false;
}
if(myAttachmentForWeapon[2] == 1){ // if silencer is on? //first digit is always 5
weaponAttachments.grip = true;
}
else{
weaponAttachments.grip = false;
}
if(myAttachmentForWeapon[3] == 1){ // if silencer is on? //first digit is always 5
weaponAttachments.redDot = true;
}
else{
weaponAttachments.redDot = false;
}
// -- applies visuals -- \\
applyWeaponAttachments();
}
If I call setupWeaponAttachments() from another function, I get that error ... why?
In the following:
> function loadInAttachmentsIntoSquads(){
>
> myAttachmentArray = currentAttachments.split(',');
>
> setupWeaponAttachments();
> }
The identifier currentAttachments is used as if it's a global variable. If it hasn't been assigned value, or its value isn't a string, at the time that the function is called, then an error will result.
So the fix is to make sure it has a string value:
function loadInAttachmentsIntoSquads(){
if (typeof currentAttachments != 'string') return;
...
}
or deal with the error some other way.
Also, where you are doing all those if..else blocks, consider:
weaponAttachments.silencer = myAttachmentForWeapon[1] == 1;
weaponAttachments.grip = myAttachmentForWeapon[2] == 1;
weaponAttachments.redDot = myAttachmentForWeapon[3] == 1;
It won't be any faster, but it is a lot less code to write and read.
You are misunderstanding/misusing the scope rules of JavaScript.
Try passing the array you're splitting explicitly and consistently, and it should solve your problem, as well as keeping the global namespace less cluttered:
First, pass the attachments in your first function explicitly:
function loadInAttachmentsIntoSquads(currentAttachments) {
var myAttachmentArray = currentAttachments.split(',');
setupWeaponAttachments(myAttachmentArray);
}
Note several things I'm doing above. First, I'm adding a currentAttachments argument to the function rather than just relying on a previously-declared global variable. Second, I'm declaring myAttachmentArray as a local variable by using the var keyword. Declaring variables with var declares them in local scope; failing to do so declares them in global scope. Third, I'm manually passing the array to the setupWeaponAttachments function, in which I will also receive the argument:
function setupWeaponAttachments(myAttachmentArray) {
var myAttachmentForWeapon = myAttachmentArray[mySquadsIndex].split('');
// [...]
}
Notice that I again properly declare the myAttachmentForWeapon variable in local scope.
If you are more careful with managing scope and properly define functions to receive the arguments they need and operate on them, you'll save yourself lots of headache in the future, and you'll get drastically fewer problems like these.

Categories

Resources