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;
}
Related
Why do I always get the last value assigned to the variable
even though I already enclosed it in a function?
When the event mouse up is triggered and getGoogleFiles is called, the last value assigned to resourceId is called. I don't get it.
for ( var i in arrayObj) {
var resourceId = arrayObj[i].ResourceId;
entity_list.onmouseup = function(event) {
parent.changeButtonState(this, event);
(function(resourceId) {
getGoogleFiles(resourceId);
})(resourceId);
}
}
Note: This is different to other JavaScript questions because the onmouseup is not triggered
I followed the creating of another function mentioned here:
JavaScript closure inside loops – simple practical example
for ( var i in arrayObj) {
entity_list.onmouseup = function(event) {
parent.changeButtonState(this, event);
testing(arrayObj[i].ResourceId);
}
}
function testing(index){
return function() { getGoogleFiles(index); };
}
But when the element of "entity_list" is triggered, nothing happens.
I can't use let because the specific browser that I'm using returns a SyntaxError
SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
Thank you!
You need to use testing() to create the listener function, not something you call inside it.
for (var i in arrayObj) {
entity_list.onmouseup = testing(arrayObj[i].ResourceId, parent);
}
function testing(index, parent) {
return function(event) {
parent.changeButtonState(this, event);
getGoogleFiles(index);
};
}
But you wouldn't have to go through any of this if you use forEach() instead of a for loop, since it creates a new scope for obj in each iteration.
arrayObj.forEach(function(obj) {
entity_list.onmouseup = function(event) {
parent.changeButtonState(this, event);
testing(obj.ResourceId);
}
});
You can't use a var scoped variable here.
But you could assign the resourceId to a data attribute on the relative html element, so you can read it when the event fires.
var arrayObj = [{ResourceId: "test1"}, {ResourceId: "test2"}, {ResourceId: "test3"}];
var entity_list = document.getElementsByClassName("entity_list");
for ( var i in arrayObj) {
entity_list[i].dataset.resourceId = arrayObj[i].ResourceId;
entity_list[i].onmouseup = function(event) {
getGoogleFiles(this.dataset.resourceId);
}
}
function getGoogleFiles(resourceId) {
console.log(resourceId);
}
<span class="entity_list">entity list (item 1)</span>
<span class="entity_list">entity list (item 2)</span>
<span class="entity_list">entity list (item 3)</span>
Maybe some of you know about AOP, in some languages using AOP can lead you to be able to inject code after, before, or while a method is executing,etc.
What I want is to apply the same in Javascript, I am currently working on a massive app which has more than 300 ajax calls, and every time I need to do some change on the catch statement on them, I have to modify them one by one which is very tedious.
What I want to do is something like :
functionName.before("try {")
functionName.after("} catch(ex){
//dostuff
}")
Is it possible? I know there are things like .call, or the arguments object inside every function..which seem pretty meta-function (AOP) functionalities.
Not with before and after, but a wrap will work:
Function.prototype.wrapTry = function(handle) {
var fn = this;
return function() {
try {
return fn.apply(this, arguments);
} catch(e) {
return handle(e);
}
};
};
Then use it like
var safeFunction = functionName.wrapTry(doStuff);
In JavaScript, functions are first-class objects. That means you can manipulate or redeclare them.
Assuming that there is a "foo" function:
var originalFoo = foo;
foo = function()
{
// "before" code.
// Call the original function.
originalFoo.apply(this, arguments);
// "after" code.
};
After that, any call to foo() will call the new function: even with parameters.
Old question but you may take a look over this https://github.com/k1r0s/kaop-ts/blob/master/docs/api.md#available-join-points
import { onException } from "kaop-ts"
import handlingException from "./somewhere"
class Something {
#onException(handlingException)
method() {
// stuff that may throw an error
}
}
I also will give a late answer in order to shed some light onto this special case that every then and now pops up as JavaScript and AOP.
Firstly, cases like the very one presented by the OP always ask for modifying already existing functionality, thus targeting closed code that sometimes is not even owned by the party that sees itself challenged from modifying the control flow of such code.
Why then, not just name it like that ... JavaScript method modification or JavaScript method modifiers.
Secondly, because of already riding the horse of terminology, altering closed functionality in JavaScript has nothing to do with Aspect-oriented Programming unless an implementation that claims to be AO provides abstraction and code-reuse levels for at least Aspect, Advice and Pointcut.
Last, for what the OP is going to achieve and what also has been the accepted answer, there does exist a a whole bunch of before, after around / wrap solutions, almost always unfortunately mentioning AO(P), and in far too many cases not taking care of the context or target which is essential to method modification.
The example I do provide uses a prototypal implementation of afterThrowing. Because JavaScript already features a standardized bind, I'm firmly convinced that Function.prototype is the right place as well for some other method-modifiers
like before, after, around, afterThrowing
and afterFinally.
// OP's example pseudo code
//
// functionName.before("try {")
//
// functionName.after("} catch(ex){
// dostuff
// }")
function doStuffAfterThrowing(exception, originalArguments) {
"use strict";
var context = this;
console.log('context : ', context);
console.log('String(exception) : ', String(exception));
console.log('originalArguments : ', originalArguments);
return "safely handled exception";
}
function doFail() {
throw (new ReferenceError);
}
function oneOutOfManyAjaxCallbacks(payload) {
doFail();
}
var jsonData = {
"foo": "foo",
"bar": "bar"
};
var someModifiedAjaxCallback = oneOutOfManyAjaxCallbacks.afterThrowing(doStuffAfterThrowing, { x: 'y' });
// does fail controlled/handled.
console.log('someModifiedAjaxCallback(jsonData) : ', someModifiedAjaxCallback(jsonData));
// does fail "Uncaught".
console.log('oneOutOfManyAjaxCallbacks(jsonData) : ', oneOutOfManyAjaxCallbacks(jsonData));
.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.afterThrowing = function (handler, target) { // afterThrowing
target = getSanitizedTarget(target);
var proceed = this ;
return (isFunction(handler) && isFunction(proceed) && function () {
var ret, args = arguments;
try {
ret = proceed.apply(target, args);
} catch (exc) {
ret = handler.call(target, exc, args);
//throw exc;
}
return ret;
}) || proceed;
};
}(Function));
</script>
Having come that far one might also consider reading ...
sandwich pattern in javascript code
Can you alter a Javascript function after declaring it?
I apologize for the amount of code, but I think this is actually a problem with AppMobi's getCurrentLocation function. Basically what happens is I delegate a tap event to each list element. Then when you tap it, it runs an asynchronous getCurrentLocation and updates some stuff. Then the next time you tap another list element, the variables bound in the callback of the getCurrentLocation Function only refer to the first time it was called. Why doesn't this work??
app = { events: [{text: "foo", time: new Date()}, {text: "bar", time: new Date()}] };
$(document).ready(refreshEvents);
function refreshEvents() {
for (var index in app.events) {
insertEventHTML(app.events[index]);
}
}
function insertEventHTML(event) {
var text = event.text;
var time = event.time;
var new_element = $('<li class="event_element"></li>');
var new_checkin_element = $('<div class="check_in_button"></div>');
new_checkin_element.bind('tap', function(e) {
check_in(e);
fade($(this), 1.0);
});
new_element.append(new_checkin_element);
var text_element = $('<div class="text_element">' + text + '</div>');
new_element.append(text_element);
var time_element = $('<div class="time_element">' + time + '</div>');
new_element.append(time_element);
$('#your_events').append(new_element);
}
function check_in(e) {
$(e.target).siblings('.time_element').text('just now');
var time = new Date(); // time and event_index are the trouble variables here
var event_index = getEventIndex(e); // the first time this function runs, event_index is correct
// then each subsequent time, it remains the same value as the first
if (!app.settings.use_location) {
app.events[event_index].times.unshift({time: time, location: null});
} else {
AppMobi.geolocation.getCurrentPosition(onLocationFound, errorFunction);
}
function onLocationFound(response) {
var lat = response.coords.latitude;
var lon = response.coords.longitude;
var last_time = app.events[event_index].times[0];
if (last_time != undefined && last_time.time == time) {
// event_index and time here will only ever refer to the first time it was called. WHY???
add_checkin(response, event_index, time);
}else{
console.log('onLocationFound was called twice');
}
}
function errorFunction(error) {
$.ui.popup({title: 'geolocation error', message: 'Geolocation error. Turn off location services in settings.'});
}
function add_checkin(response, event_index, time) {
// and then of course event_index and time are wrong here as well. I don't get it.
app.events[event_index].times.unshift(
{
time: time,
location: {
latitude: response.coords.latitude,
longitude: response.coords.longitude
}
});
AppMobi.cache.setCookie('app', JSON.stringify(app), -1);
}
}
function getEventIndex(e) {
var target = $(e.target).parent();
var siblings = target.parent().children('li');
for (var i = 0; i < siblings.length; i++) {
if ($(target)[0].offsetTop == $(siblings[i])[0].offsetTop) {
return i;
}
}
}
Well, your issue seems to be that you are declaring a private variable event_index inside the check_in function and try to resolve it's value by accessing a global event_index variable inside onLocationFound.
Here's what you could do instead:
AppMobi.geolocation.getCurrentPosition(function (response) {
onLocationFound(response, event_index);
}, errorFunction);
function onLocationFound(response, event_index) { //... }
EDIT:
it is declared within check_in...
You are right, I totally missed that somehow. Well in that case it's very unlikely that the event_index variable inside onLocationFound isin't the same as in check_in. Do a console.log(event_index) inside onLocationFound and it should be the same. The only way it could be different is if you modify the local variable before the handler is called, which you doesn't seem to do, or if getCurrentPosition stores the first handler somehow and ignores subsequent handlers, but this API wouldn't make any sense.
EDIT 2:
As we suspect the handler might not be registered correctly the second time, I would suggest to check this way:
function check_in() {
if (!check_in.called) {
check_in.called = true;
check_in.onLocationFound = onLocationFound;
}
//...
function onLocationFound() {
console.log(arguments.callee === check_in.onLocationFound);
}
}
You can also simple do onLocationFound.version = new Date() and check arguments.callee.version to see if it stays the same.
I know you marked this answered already but....it might not actually be a problem with the library. Can I direct you to a post by #Joel Anair where he has posted an article and the example number five seems to be the "gotcha" which might have gotcha ;)
How do JavaScript closures work?
Basically in the for loop they are all being set to the same reference of i so event_index will all be the same value/reference. (which explains why they're all the same).
Ok, difficult to understand from the title only. Here is an example. I want a function to refer to a variable that is "injected" automagically, ie:
function abc() {
console.log(myVariable);
}
I have tried with:
with({myVariable: "value"}) { abc() }
but this doesn't work unless abc is declared within the with block, ie:
with({myVariable: "value"}) {
function abc() {
console.log(myVariable);
}
abc(); // This will work
}
So the last piece will work, but is it possible to fake the with statement, or do I have to force the developers to declare their function calls in a with statement?
Basically the call I want to do is:
doSomething({myVariable: "value"}, function() {
console.log(myVariable);
});
Ofcourse, I am aware I could pass this is a one parameter object, but that is not what I am trying to do:
doSomething({myVariable: "value"}, function(M) {
console.log(M.myVariable);
});
Further more, I am trying to avoid using eval:
with({myVariable: "value"}) {
eval(abc.toString())(); // Will also work
}
Is this not supported at at all beyond eval in Javascript?
JavaScript does not provide any straightforward way to achieve the syntax you're looking for. The only way to inject a variable into a Lexical Environment is by using eval (or the very similar Function constructor). Some of the answers to this question suggest this. Some other answers suggest using global variables as a workaround. Each of those solutions have their own caveats, though.
Other than that, your only option is to use a different syntax. The closest you can get to your original syntax is passing a parameter from doSomething to the callback, as Aadit M Shah suggested. Yes, I am aware you said you don't want to do that, but it's either that or an ugly hack...
Original answer (written when I didn't fully understand the question)
Maybe what you're looking for is a closure? Something like this:
var myVariable = "value";
function doSomething() {
console.log(myVariable);
};
doSomething(); // logs "value"
Or maybe this?
function createClosure(myVariable) {
return function() {
console.log(myVariable);
};
}
var closure = createClosure("value");
closure(); // logs "value"
Or even:
var closure = function(myVariable) {
return function() {
console.log(myVariable);
};
}("value");
closure(); // logs "value"
I asked a similar question a long time ago: Is it possible to achieve dynamic scoping in JavaScript without resorting to eval?
The short answer is no, you can't achieve dynamic scoping without resorting to eval. The long answer is, you don't need to.
JavaScript doesn't support dynamic scoping, but that's not an issue because you can make your free variables parameters of the function that they belong to.
In my humble opinion this is the best solution:
function doSomething(context, callback) {
callback(context);
}
doSomething({myVariable: "value"}, function(M) {
console.log(M.myVariable);
});
However since you don't want to write a formal parameter, the next best thing is to use this instead:
function doSomething(context, callback) {
callback.call(context);
}
doSomething({myVariable: "value"}, function() {
console.log(this.myVariable);
});
Another option would be to manipulate the formal parameter list of the program as follows:
function inject(func, properties) {
var args = [], params = [];
for (var property in properties) {
if (properties.hasOwnProperty(property)) {
args.push(properties[property]);
params.push(property);
}
}
return Function.apply(null, params.concat("return " + func.toString()))
.apply(null, args);
}
Now we can use this inject method to inject properties into a function as follows:
function doSomething(context, callback) {
var func = inject(callback, context);
func();
}
doSomething({myVariable: "value"}, function() {
console.log(myVariable);
});
See the demo: http://jsfiddle.net/sDKga/1/
Note: The inject function will create an entirely new function which will not have the same lexical scope as the original function. Hence functions with free variables and partially applied functions will not work as expected. Only use inject with normal functions.
The Function constructor is kind of like eval but it's much safer. Of course I would advise you to simply use a formal parameter or this instead. However the design decision is your choice.
Try:
function doSomething(vars, fun) {
for (var key in vars) { // set the variables in vars
window[key] = vars[key];
}
fun.call(); // call function
for (var key in vars) { // remove the variables again. this will allow only the function to use it
delete window[key];
}
}
Set global variables that can then be received inside of fun
The JSFiddle: http://jsfiddle.net/shawn31313/MbAMQ/
Warning: disgusting code ahead
function callWithContext(func, context, args) {
var oldProperties = {};
for(var n in context) {
if(context.hasOwnProperty(n)) {
var oldProperty = Object.getOwnPropertyDescriptor(self, n);
oldProperties[n] = oldProperty;
(function(n) {
Object.defineProperty(self, n, {
get: function() {
if(arguments.callee.caller === func) {
return context[n];
}
if(!oldProperty) {
return;
}
if(oldProperty.get) {
return oldProperty.get.apply(this, arguments);
}
return oldProperty.value;
},
set: function(value) {
if(arguments.callee.caller === func) {
context[n] = value;
}
if(!oldProperty) {
return;
}
if(oldProperty.set) {
return oldProperty.get.apply(this, arguments);
} else if(!oldProperty.writable) {
var fakeObject = {};
Object.defineProperty(fakeObject, n, {value: null, writable: false});
fakeObject[n] = value; // Kind of stupid, but…
return;
}
oldProperty.value = value;
}
});
})(n);
}
}
func.apply(this, args);
for(var n in context) {
if(context.hasOwnProperty(n)) {
if(oldProperties[n]) {
Object.defineProperty(self, n, oldProperties[n]);
} else {
delete self[n];
}
}
}
}
This is vomitously horrendous, by the way; don’t use it. But ew, it actually works.
i don't see why you can't just pass the info in or define a single global, but i think that would be best.
that said, i am working on a Module maker/runner that allows sloppy/dangerous code to execute without interference to the host environment. that provides the opportunity to re-define variables, which can be passed as an object.
this does use eval (Function() technically) but it can run in "use strict", so it's not too crazy/clever.
it doesn't leave behind artifacts.
it also won't let globals get hurt.
it's still a work in progress, and i need to iron out a couple minor details before i vouch for security, so don't use it for fort knox or anything, but it's working and stable enough to perform the operation asked for.
tested in ch28, FF22, IE10:
function Module(strCode, blnPreventExtensions, objWhitelist, objExtend) {
var __proto__=self.__proto__, pbu=self.__proto__, str=strCode, om=[].map, wasFN=false,
params = {Object:1}, fnScrubber, natives= [ Object, Array, RegExp, String, Boolean, Date] ,
nativeSlots = [],
preamble = "'use strict';" ,
inherited="__defineGetter__,__defineSetter__,__proto__,valueOf,constructor,__lookupGetter__,__lookupSetter__",
late = inherited +
Object.getOwnPropertyNames(__proto__||{}) + Object.getOwnPropertyNames(window);
late.split(",").sort().map(function(a) {
this[a] = 1;
}, params);
preamble+=";var "+inherited+";";
//turn functions into strings, but note that a function was passed
if(str.call){wasFN=true; str=String(str); delete params.Object; }
objExtend=objExtend||{};
var vals=Object.keys(objExtend).map(function(k){ return objExtend[k]; })
// build a usable clone of Object for all the new OOP methods it provides:
var fakeOb=Object.bind();
(Object.getOwnPropertyNames(Object)||Object.keys(Object)).map(function(a){
if(Object[a] && Object[a].bind){this[a]=Object[a].bind(Object); } return this;
},fakeOb)[0];
//allow "eval" and "arguments" since strict throws if you formalize them and eval is now presumed safe.
delete params.eval;
delete params.arguments;
params.hasOwnProperty=undefined;
params.toString=undefined;
params['__proto__']={};
__proto__=null;
Object.keys(objWhitelist||{}).map(function ripper(a,b){
b=this[a];
if(typeof b!=='object'){
delete this[a];
}
}, params);
// var ok=Object.keys.bind(Object);
// prevent new prototype methods from being added to native constructors:
if (blnPreventExtensions) {
natives.forEach(function(con, i) {
var proto=con.prototype;
Object.getOwnPropertyNames(proto).map(function(prop){
if(proto[prop] && proto[prop].bind ){ this[prop]=proto[prop];}
}, nativeSlots[i] = {});
delete con.constructor;
delete con.prototype.constructor;
}); //end con map()
} /* end if(blnPreventExtensions) */
//white-list harmless math utils and prevent hijacking:
delete params.Math;
if(blnPreventExtensions){Object.freeze(Math);}
//prevent literal constructors from getting Function ref (eg: [].constructor.constructor, /./.constructor.constructor, etc...):
Function.prototype.constructor = null;
try {
//generate a private wrapper function to evaluate code:
var response = Function(
Object.keys(objExtend) + (vals.length?",":"") +
Object.keys(params).filter(/./.test, /^[\w\$]+$/), // localize most globals
preamble + " return " + str.trim() // cram code into a function body with global-blocking formal parameters
);
// call it with a blank this object and only user-supplied arguments:
if (blnPreventExtensions) { //( user-land code must run inside here to be secure)
response = response.apply({}, vals.concat(fakeOb)).apply({}, [].slice.call(arguments,4) );
}else{
response = response.apply({}, vals.concat(fakeOb));
}
} catch (y) {
response = y + "!!";
} /* end try/catch */
if (blnPreventExtensions) {
om.call(natives, function(con, i) {
var pro=con.prototype;
//remove all proto methods for this con to censor any additions made by unsafe code:
Object.getOwnPropertyNames(pro).map(function(a){ try{delete pro[a];}catch(y){}});
//restore all original props from the backup:
var bu = nativeSlots[i];
om.call(Object.keys(bu), function(prop){ con.prototype[prop]=bu[prop]; }, bu);
}); //end con map()
} /* end if(blnPreventExtensions) */
//restore hidden Function constructor property:
Function.prototype.constructor = Function;
return response;
} /* end Module() */
/////////////////////////////////////////////////////////////
function doSomething(context, fn){
console.log(myVariable);
return myVariable;
}
//use 1:
alert( Module(doSomething, true, {console:1}, {myVariable: "value123"} ) );// immed
//use2:
var fn=Module(doSomething, false, {console:1}, {myVariable: "value123"} );// as function
alert(fn);
alert(fn());
again, i think OP would be best off not doing things later than need be, but for the sake of comprehensiveness and inspiration i'm putting this out there in good faith.
You need to use call() to construct a context, as in:
var f=function(){
console.log(this.foo);
};
f.call({foo:'bar'})
will print "bar"
You can avoid using eval() in calling the function, if you are willing to use it in doSomething():
function abc() {
console.log(myVariable);
}
// Prints "value"
callWith({ myVariable: "value" }, abc);
function callWith(context, func) {
for(var i in context) eval('var ' + i + ' = context[i];');
eval('(' + func.toString() + ')')();
}
Have a look at this post.
Have a look at goog.partial, scroll a little bit up to see the description of what it does:
Here is an implementation of it:
var b = goog.partial(alert, 'Hello world!');
b();//alerts "Hello world!"
In the example it passes the function alert with parameter "Hello world!" but you can pass it your own function with multiple parameters.
This allows you to create a variable that points to a function that is always called with a certain paramater. To use parameters in a function that are not named you can use arguments:
function test(){
console.log(arguments);//["hello","world"]
}
test("hello","world");
I have a global variable NS which I can access from the console as such:
NS.some_func();
NS is populated using a method called extendSafe()
some_scope.extendSafe = function (o1, o2) {
var key;
for (key in o2) {
if (o2.hasOwnProperty(key) && o1.hasOwnProperty(key)) {
throw "naming collision: " + key;
}
o1[key] = o2[key];
}
return o1;
};
This is used by setting up a public scope called $P and then copying over to the global scope NS once all the $P methods have been defined.
I want to to it this way so I can verify that I'm not writing over any properties.
This worked well until I tried to save a local variable to $P for later copying to NS. Because the interpreter does not know that $P will be "released" to the window scope, it does not know to keep the local variable active. So I can not use my safeExtend method.
I verified this was the issue by doing a direct copy as such:
NS.local = local;
I can now access NS.local from the console.
However if I copy it over as I wish to do:
$P.local = local;
extendSafe(NS, $P);
The local variable is not available.
How can I safely release it, i.e. using safeExtend()?
Code Snippet
Issue is commented as
// hacked needs a fix
$P.machine = function (obj) {
var pipe,
data_send,
ajax_type,
wait_animation,
set;
wait_animation = document.getElementById('wait_animation');
set = false;
pipe = NS.makePipe(obj);
if ($R.Parsel[pipe.model] === undefined) {
return;
}
time('start');
if ($R.Parsel[pipe.model].hasOwnProperty("pre")) {
pipe = $R.Parsel[pipe.model].pre(pipe);
} else {
return;
}
if (pipe.form_data) {
ajax_type = 'multi';
var form_data = pipe.form_data;
delete pipe.form_data;
form_data.append("pipe", JSON.stringify(pipe));
data_send = form_data;
} else {
ajax_type = 'post';
data_send = 'pipe=' + encodeURIComponent(JSON.stringify(pipe));
}
if (pipe.state === true) {
time('middle');
if (wait_animation) {
set = true;
wait_animation.style.opacity = 1;
}
NS.ajax({
type: ajax_type,
url: NS.Reg.get('path') + NS.Reg.get('path_ajax'),
data: data_send,
callback: function (pipe_string_receive) {
var pass_prefix = pipe_string_receive.slice(0, 3),
times;
if (wait_animation && set) {
wait_animation.style.opacity = 0;
}
if (pass_prefix === '|D|') {
NS.log('|DEBUG| ' + pipe_string_receive.slice(3));
} else if (pass_prefix === '|A|') {
time('middle');
pipe = JSON.parse(pipe_string_receive.slice(3));
if ($R.Parsel[pipe.model].hasOwnProperty("post")) {
pipe = $R.Parsel[pipe.model].post(pipe);
times = time('finish');
pipe.time.pre = times[0];
pipe.time.transit = times[1];
pipe.time.post = times[2];
// works but hacked needs a fix
NS.last = pipe;
// will not exendSafe()
$P.last = pipe;
} else {
return;
}
} else {
throw "<No 'A' or 'D'>" + pipe_string_receive;
}
}
});
}
};
I see you've solved the problem, but I have a feeling that there's something you're misunderstanding about JavaScript:
This worked well until I tried to save a local variable to $P for later copying to NS. Because the interpreter does not know that $P will be "released" to the window scope, it does not know to keep the local variable active. So I can not use my safeExtend method.
I verified this was the issue by doing a direct copy as such:
NS.local = local;
I can now access NS.local from the console.
However if I copy it over as I wish to do:
$P.local = local;
extendSafe(NS, $P);
The local variable is not available.
How can I safely release it, i.e. using safeExtend()?
This doesn't make sense. JavaScript is very good at keeping track of references to objects. If there are any references to an object, it won't garbage collect the object. I have no idea what it could mean to "release an object to the window scope". There isn't really any such concept, just objects and references to them.
I tried looking through your original code, but there's a lot of code there that isn't related to the problem. If you were to simplify it to a minimal test case, I'll bet a simpler solution would become evident.
I do see one issue in your smaller snippet above. You defined your extendSafe() function as some_scope.extendSafe(), but here you're calling it with a plain extendSafe() call and no reference to some_scope. Did it actually call the function? Is this just a typo in the smaller example?
Of course, if you're just happy to have found a solution and want to move on, that's quite understandable! I just have a strong feeling that there's extra code here that you don't need.