Access to other variables inside of jeditable - javascript

I'm really new to javascript, so sorry for my ignorance. In jeditables, you can specify a callback function. I'm using all this code in a separate script. Is there a way to pass variables into this callback function? for example:
var info = "foo";
$('#bar').editable("/foo/bar",
callback : function(value, settings) {
var foobar = value + info;
});

var info = "foo";
$('#bar').editable("/foo/bar",
function(value, settings) {
var foobar = value + info;
});
You should read up on javascript scoping.
What I did above is not usually the way to go since info is now in the global scope.
Side point:
You can even move you callback to a completely different location:
var info = "foo",
callBackFn = function(v, s){
var foobar = v + info;
};
$('#bar').editable("/foo/bar", callBackFn);

You can also assign attributes to the "settings" object as follows:
$(".myClass").editable( "/foo/bar",
{
indicator: "Saving...",
tooltip: "Click to edit",
onblur: "submit",
bax: bax
},
function(value, settings) {
var foobar = value + settings.bax;
});
Inside your handler, you can see the use of the reference to the object by simply stating settings.bax

Related

Use string to access variable in javascript

If I have:
name.sub = function() {
var sub = {};
var placeholder = "test"
var test = function() {
return 42;
};
// Desired code would be here
return sub;
};
I want to use a placeholder to access the variable so that I get 42.
Something like
window["name"]["sub"][placeholder] is seemingly looking for name.sub.test.
The only answers I found were if it was a global variable.
Using eval would work, but I've heard it should be avoided where possible.
placeholder = "test";
console.log(eval(placeholder + '()'))
// Would return 42
My actual end goal is to have an associative array where:
console.log(array[placeholder]);
// Would return 42
Any help would be appreciated.
This is what I ended up using for anyone interested:
name.sub= function() {
var sub = {};
var placeholder = "test"
var test = function() {
return 42;
var newObj = {};
newObj["test"] = function() {test()}
console.log(newObj[placeholder]())
// Should return 42
};
You can't access variables inside a function from outside said function.
Instead, you could do this:
name.sub = function(placeholder) {
var functions = {
"test": function() {
return 42;
},
};
return functions[placeholder]();
};
name.sub("test"); // 42
I'm not sure if that's what you're looking for but hopefully it is. Explain more?
You can't access local variables inside a function the same way you can access properties of window in global scope.
kangax wrote an interesting article about Understanding delete [in JavaScript], which includes an explanation of what Activation objects are - which I think is what you're looking for.
I suggest you read the entire article, but long story short:
Inside functions, declared variables (and functions) are added as properties to the activation object of the current scope, as they get added to window in the global scope.
But unlike window:
Note that Activation object is an internal mechanism and is never really accessible by program code.
Conclusion: What you're asking is not (currently) possible.
Your options are limited to:
Using an intermediate object:
name.sub = function() {
var sub = {};
var placeholder = "test";
var array = {};
array.test = function() {
return 42;
};
console.log(array[placeholder]());
return sub;
};
Using eval, exactly like you suggested:
name.sub = function() {
var sub = {};
var placeholder = "test";
var test = function() {
return 42;
};
console.log(eval(placeholder + '()'));
return sub;
};
Using window, by removing var from test's declaration:
name.sub = function() {
var sub = {};
var placeholder = "test";
test = function() {
return 42;
};
console.log(window[placeholder]());
return sub;
};
I suggest the first option for the sake of performance over eval, for compatibility over window (might collide with other code), and simply because of personal taste and what I consider good practice.
By what i understand of your question,
its seem like you are looking for a key/value pair to a JavaScript object literal,
window.name is reserved btw: https://developer.mozilla.org/en-US/docs/Web/API/Window/name
var sub = {
'test': function() {
return 42;
},
'test2': 42;
}
sub['test']();
sub['test2'];
add by
Using dot notation:
sub.test3 = "value3";
Using square bracket notation:
sub["test4"] = "value4";
Maybe im thinking to simple , but seems like this is what you are looking for

Privileged Methods - how to get values of properties that are passed to the function?

(example is from the book but I don't seem to get it)
function User (properties){
for( var i in properties){
(function(){
this["get"+i] = function () { return properties[i];};
this["set"+i] = function (valueOne) { properties[i] = valueOne; };
}) ();
}// END for
}// END User
var userOne = new User ({ name: "Billy", age: 35 });
userOne.getname();
When I run this, User does not have getname method. How can I make the privileged method works?
You need both this and i captured in the closure:
function User (properties){
for( var i in properties){
(function(t, i){
t["get"+i] = function () { return properties[i];};
t["set"+i] = function (valueOne) { properties[i] = valueOne; };
}) (this, i);
}// END for
}// END User
this is not who you think it is. Since you invoked the IIFE on nothing, this will be the global scope, so the window will get the methods getname, etc, which is not what you expected.
To fix it, if you want to keep the IIFE, you need to call it on the right context:
function User (properties){
for( var i in properties){
(function(key){
this["get"+key] = function () { return properties[key];};
this["set"+key] = function (valueOne) { properties[key] = valueOne; };
}).call(this, i);
}// END for
}// END User
var userOne = new User ({ name: "Billy", age: 35 });
userOne.getname();
Note that you also forgot to pass the i argument to the function and to interpret it as parameter. Otherwise all the functions would get bound to the same key, thus userOne.getname would return 35.
This is because you used an immediately invoked function
for( var i in properties){
(function(){ //<--- This
this["get"+i] = function () { return properties[i];};
this["set"+i] = function (valueOne) { properties[i] = valueOne; };
}) ();
}
Remove it and it will still not work, but your methods will be there. To get it fully working you should preserve i
for( var i in properties){
(function(i){ //<--- This
this["get"+i] = function () { return properties[i];};
this["set"+i] = function (valueOne) { properties[i] = valueOne; };
}) (i); //<--- and this
}
The latter issue is not as interesting (though related to) the first one.
Javascript has only what is known as "function scope" meaning that the only thing that limits function scope is...well...a function. Therefore, a common pattern is to use IIFEs like this inside of for loops or in many places where you don't want variables to leak.
However, the this parameter in javascript is weird. Understand the following and it will save you a ton of hassle: this in javascript is no different from any other parameter.
Let me explain.
There are four ways to invoke a function in javascript.
myFn.call("this param", "param 1", "param 2"); //this is "this param"
myFn.apply("this param", ["param 1", "param 2"]); //this is "this param"
myFn("param 1", "param 2");
//javascript takes a guess at what `this` should be -
//usually it is set to the global `window`.
new myFn("param 1", "param 2");
//`this` is a new function with its' prototype set to myFn.prototype
If you were always to use the .call form all ambiguity would be gone and you can see that this is exactly like every other parameter. However that's extra syntax and people prefer using the simpler form which means you have to consider the rules for what is "this".
Therefore what you are doing in your example is placing getters and setters on the global window object.
I'm going to make a statement here that your book probably doesn't agree with but that I've picked up from years of learning, working with, and teaching javascript:
Don't use the new and this keywords.
These two keywords introduce a ton of concepts into JS that are confusing and really - unless you're making something very performance sensitive (you're not, I know you think you are, but you're not) - not necessary. Instead create new objects simply like this:
var user = { name: "Billy", age: 35 };
if you absolutely must have getters and setters this will do it:
function createObjectWithProps (properties){
var obj = {};
var state = {}[
for( var k in properties){
(function(key) {
obj["get"+key] = function () { return state[key];};
obj["set"+key] = function (valueOne) { state[key] = valueOne; };
})(k)
}
return obj;
}
var userOne = createObjectWithProps ({ name: "Billy", age: 35 });
userOne.getname();
Although I'll go even further to state that getters and setters are not terribly useful in js, and when you DO use them it is standard to follow a pattern similar to what knockout does.
The problem is the "this" keyword.
Because you are using it inside an immediately invoked function it is pointing to the global scope.
try this:
function User (properties){
for( var i in properties){
(function(self,i){
self["get"+i] = function () { return properties[i];};
self["set"+i] = function (valueOne) { properties[i] = valueOne; };
}) (this,i);
}// END for
}// END User

send parametres as values using JSON syntax

i want to send an url with parametres, those parametres are values taken by a form with javascript and i want to use JSON to do it, but when i debug i see this error : Uncaught ReferenceError: name is not defined..
function recup()
{
var selectElmt = document.getElementById("name");
var selectcat = document.getElementById("msg");
var name = selectElmt.options[selectElmt.selectedIndex].value;
var msg = selectcat.options[selectcat.selectedIndex].value;
}
function go() { // button send who call the function go
var p_url="http://mysite.com/class?name=" + name + "&message=" + msg +
$.getJSON(p_url, {
}).done(function( data ) {
$.each(data, function (key, field) {
alert(field);
});
});
return false;
}
it's a syntax error when calling the value name and msg but i don"t know how to fix it or in the go function
You two errors, closing curly brace and plus character, the code shoud be:
var msg = "hello"; // i just simplified the value
var name = "test";
function go() { // button send who call the function go
var p_url="http://mysite.com/class?name=" + name + "&message=" + msg;
$.getJSON(p_url, {
}).done(function( data ) {
$.each(data, function (key, field) {
alert(field);
});
});
return false;
}
UPDATE: You need to make name and msg global:
var name, msg;
function recup() {
var selectElmt = document.getElementById("name");
var selectcat = document.getElementById("msg");
name = selectElmt.options[selectElmt.selectedIndex].value;
msg = selectcat.options[selectcat.selectedIndex].value;
}
function go() { // button send who call the function go
var p_url="http://mysite.com/class?name=" + name + "&message=" + msg;
$.getJSON(p_url, {
}).done(function( data ) {
$.each(data, function (key, field) {
alert(field);
});
});
return false;
}
and recup need to be executed before go
the two variables are in another function
Well, that explains it. A variable that is local to a function cannot be accessed by another function.
You have to define the variables in a scope that is shared by both functions. This could be the global scope, but you should avoid creating global variables (you cannot have a global variable with name name anyways, because it exists already).
If you want to assign a value to a variable in a higher scope, use name = ...; instead of var name = ...;.
Example:
(function() {
// create a new scope so that we don't pollute the global scope
// this variable can be accessed by both functions
var answer;
function foo() {
// don't use `var` here, otherwise you create a local variable which
// shadows the variable with the same name in a higher scope
answer = 42;
}
function bar() {
alert(answer);
}
foo();
bar();
}());

Is it possible to restrict the scope of a javascript function?

Suppose I have a variables in the global scope.
Suppose I wish to define a function which I can guarantee will not have access to this variable, is there a way to wrap the function, or call the function, that will ensure this?
In fact, I need any prescribed function to have well defined access to variables, and that access to be defined prior to, and separate from that function definition.
Motivation:
I'm considering the possibility of user submitted functions. I should be able to trust that the function is some variety of "safe" and therefore be happy publishing them on my own site.
Run the code in an iframe hosted on a different Origin. This is the only way to guarantee that untrusted code is sandboxed and prevented from accessing globals or your page's DOM.
Using embedded Web Workers could allow to run safe functions. Something like this allows a user to enter javascript, run it and get the result without having access to your global context.
globalVariable = "I'm global";
document.getElementById('submit').onclick = function() {
createWorker();
}
function createWorker() {
// The text in the textarea is the function you want to run
var fnText = document.getElementById('fnText').value;
// You wrap the function to add a postMessage
// with the function result
var workerTemplate = "\
function userDefined(){" + fnText +
"}\
postMessage(userDefined());\
onmessage = function(e){console.log(e);\
}"
// web workers are normally js files, but using blobs
// you can create them with strings.
var blob = new Blob([workerTemplate], {
type: "text/javascript"
});
var wk = new Worker(window.URL.createObjectURL(blob));
wk.onmessage = function(e) {
// you listen for the return.
console.log('Function result:', e.data);
}
}
<div>Enter a javascript function and click submit</div>
<textarea id="fnText"></textarea>
<button id="submit">
Run the function
</button>
You can try these for example by pasting it in the textarea:
return "I'm a safe function";
You can see that it's safe:
return globalVariable;
You can even have more complex scripts, something like this:
var a = 4, b = 5;
function insideFn(){
// here c is global, but only in the worker context
c = a + b;
}
insideFn();
return c;
See info about webworkers here, especially embedded web workers:
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Embedded_workers
A little late, but maybe it will help you a bit
function RestrictFunction(params) {
params = ( params == undefined ? {} : params );
var scope = ( params.scope == undefined ? window : params.scope );
var data = ( params.data == undefined ? {} : params.data );
var script = ( params.script == undefined ? '' : params.script );
if (typeof params.script == 'function') {
script = params.script.toString();
script = script.substring(script.indexOf("{") + 1, script.lastIndexOf("}"));
}
// example: override native functions that on the white list
var setTimeout = function(_function,_interval) {
// this is important to prevent the user using `this` in the function and access the DOM
var interval = scope.setTimeout( function() {
RestrictFunction({
scope:scope,
data:data,
script:_function
});
} , _interval );
// Auto clear long user intervals
scope.setTimeout( function() {
scope.clearTimeout(interval);
} , 60*1000 );
return interval;
}
// example: create custom functions
var trace = function(str) {
scope.console.log(str);
}
return (function() {
// remove functions, objects and variables from scope
var queue = [];
var WhiteList = [
"Blob","Boolean","Date","String","Number","Object","Array","Text","Function",
"unescape","escape","encodeURI","encodeURIComponent","parseFloat","parseInt",
"isNaN","isFinite","undefined","NaN",
"JSON","Math","RegExp",
"clearTimeout","setTimeout"
];
var properties = Object.getOwnPropertyNames(scope);
for (var k = 0; k<properties.length; k++ ) {
if (WhiteList.indexOf(properties[k])!=-1) continue;
queue.push("var "+properties[k]+" = undefined;");
}
for (var k in scope) {
if (WhiteList.indexOf(k)!=-1) continue;
queue.push("var "+k+" = undefined;");
}
queue.push("var WhiteList = undefined;");
queue.push("var params = undefined;") ;
queue.push("var scope = undefined;") ;
queue.push("var data = undefined;") ;
queue.push("var k = undefined;");
queue.push("var properties = undefined;");
queue.push("var queue = undefined;");
queue.push("var script = undefined;");
queue.push(script);
try {
return eval( '(function(){'+ queue.join("\n") +'}).apply(data);' );
} catch(err) { }
}).apply(data);
}
Example of use
// dummy to test if we can access the DOM
var dummy = function() {
this.notify = function(msg) {
console.log( msg );
};
}
var result = RestrictFunction({
// Custom data to pass to the user script , Accessible via `this`
data:{
prop1: 'hello world',
prop2: ["hello","world"],
prop3: new dummy()
},
// User custom script as string or function
script:function() {
trace( this );
this.msg = "hello world";
this.prop3.notify(this.msg);
setTimeout( function() {
trace(this);
} , 10 );
trace( data );
trace( params );
trace( scope );
trace( window );
trace( XMLHttpRequest );
trace( eval );
return "done!"; // not required to return value...
},
});
console.log( "result:" , result );
You can't restrict the scope of a Function using the "call" or "apply" methods, but you can use a simple trick using "eval" and scoping to essentially hide any specific global variables from the function to be called.
The reason for this is because the function has access to the "global" variables that are declared at the scope that the function itself what declared. So, by copying the code for the method and injecting it in eval, you can essentially change the global scope of the function you are looking to call. The end result is essentially being able to somewhat sandbox a piece of javascript code.
Here's a full code example:
<html>
<head>
<title>This is the page title.</title>
<script>
function displayTitle()
{
alert(document.title);
}
function callMethod(method)
{
var code = "" +
// replace global "window" in the scope of the eval
"var window = {};" +
// replace global "document" in the scope of the eval
"var document = {}; " +
"(" +
// inject the Function you want to call into the eval
method.toString() +
// call the injected method
")();" +
"";
eval(code);
}
callMethod(displayTitle);
</script>
</head>
<body></body>
</html>
The code that gets eval'd looks like this:
var window = {};
var document = {};
(function displayTitle()
{
alert(document.title);
})();
You can use WebWorkers to isolate your code:
Create a completely separate and parallel execution environment (i.e. a separate thread or process or equivalent construct), and run the rest of these steps asynchronously in that context.
Here is a simple example:
someGlobal = 5;
//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo) {
return window.URL.createObjectURL(new Blob([foo], {
type: 'text/javascript'
}));
}
function protectCode(code) {
var worker = new Worker(getScriptPath(code));
}
protectCode('console.log(someGlobal)'); // prints 10
protectCode('console.log(this.someGlobal)');
protectCode('console.log(eval("someGlobal"))');
protectCode('console.log(window.someGlobal)');
This code will return:
Uncaught ReferenceError: someGlobal is not defined
undefined
Uncaught ReferenceError: someGlobal is not defined and
Uncaught ReferenceError: window is not defined
so you code is now safe.
EDIT: This answer does not hide the window.something variables. But it has a clean way to run user-defined code. I am trying to find a way to mask the window variables
You can use the javascript function Function.prototype.bind() to bind the user submitted function to a custom scope variable of your choosing, in this custom scope you can choose which variables to share with the user defined function, and which to hide. For the user defined functions, the code will be able to access the variables you shared using this.variableName. Here is an example to elaborate on the idea:
// A couple of global variable that we will use to test the idea
var sharedGlobal = "I am shared";
var notSharedGlobal = "But I will not be shared";
function submit() {
// Another two function scoped variables that we will also use to test
var sharedFuncScope = "I am in function scope and shared";
var notSharedFuncScope = "I am in function scope but I am not shared";
// The custom scope object, in here you can choose which variables to share with the custom function
var funcScope = {
sharedGlobal: sharedGlobal,
sharedFuncScope: sharedFuncScope
};
// Read the custom function body
var customFnText = document.getElementById("customfn").value;
// create a new function object using the Function constructor, and bind it to our custom-made scope object
var func = new Function(customFnText).bind(funcScope);
// execute the function, and print the output to the page.
document.getElementById("output").innerHTML = JSON.stringify(func());
}
// sample test function body, this will test which of the shared variables does the custom function has access to.
/*
return {
sharedGlobal : this.sharedGlobal || null,
sharedFuncScope : this.sharedFuncScope || null,
notSharedGlobal : this.notSharedGlobal || null,
notSharedFuncScope : this.notSharedFuncScope || null
};
*/
<script type="text/javascript" src="app.js"></script>
<h1>Add your custom body here</h1>
<textarea id="customfn"></textarea>
<br>
<button onclick="submit()">Submit</button>
<br>
<div id="output"></div>
The example does the following:
Accept a function body from the user
When the user clicks submit, the example creates a new function object from the custom body using the Function constructor. In the example we create a custom function with no parameters, but params can be added easily as the first input of the Function constructor
The function is executed, and its output is printed on the screen.
A sample function body is included in comments, that tests which of the variables does the custom function has access to.
Create a local variable with the same name.
If you have a global variable like this:
var globalvar;
In your function:
function noGlobal(); {
var globalvar;
}
If the function refers to globalvar, it will refers to the local one.
In my knowledge, in Javascript, any variable declared outside of a function belongs to the global scope, and is therefore accessible from anywhere in your code.
Each function has its own scope, and any variable declared within that function is only accessible from that function and any nested functions. Local scope in JavaScript is only created by functions, which is also called function scope.
Putting a function inside another function could be one possibility where you could achieve reduced scope ( ie nested scope)
if you are talking about a function that is exposed to you by loading a third party script, you are pretty much out of luck. that's because the scope for the function is defined in the source file it's defined in. sure, you can bind it to something else, but in most cases, that's going to make the function useless if it needs to call other functions or touch any data inside it's own source file - changing it's scope is only really feasible if you can predict what it needs to be able to access, and have access to that yourself - in the case of a third party script that touches data defined inside a closure, object or function that's not in your scope, you can't emulate what might need.
if you have access to the source file then it's pretty simple - look at the source file, see if it attempts to access the variable, and edit the code so it can't.
but assuming you have the function loaded, and it doesn't need to interact with anything other than "window", and for academic reasons you want to do this, here is one way - make a sandbox for it to play in. here's a simple shim wrapper that excludes certain items by name
function suspectCode() {
console.log (window.document.querySelector("#myspan").innerText);
console.log('verbotten_data:',typeof verbotten_data==='string'?verbotten_data:'<BANNED>');
console.log('secret_data:',typeof secret_data==='string'?secret_data:'<BANNED>'); // undefined === we can't
console.log('window.secret_data:',typeof window.secret_data==='string'?window.secret_data:'<BANNED>');
secret_data = 'i changed the secret data !';
console.log('secret_data:',typeof secret_data==='string'?secret_data:'<BANNED>'); // undefined === we can't
console.log('window.secret_data:',typeof window.secret_data==='string'?window.secret_data:'<BANNED>');
}
var verbotten_data = 'a special secret';
window.secret_data = 'special secret.data';
console.log("first call the function directly");
suspectCode() ;
console.log("now run it in a sandbox, which banns 'verbotten_data' and 'secret_data'");
runFunctionInSandbox (suspectCode,[
'verbotten_data','secret_data',
// we can't touch items tied to stack overflows' domain anyway so don't clone it
'sessionStorage','localStorage','caches',
// we don't want the suspect code to beable to run any other suspect code using this method.
'runFunctionInSandbox','runSanitizedFunctionInSandbox','executeSandboxedScript','shim',
]);
function shim(obj,forbidden) {
const valid=Object.keys(obj).filter(function(key){return forbidden.indexOf(key)<0;});
var shimmed = {};
valid.forEach(function(key){
try {
shimmed[key]=obj[key];
} catch(e){
console.log("skipping:",key);
}
});
return shimmed;
}
function fnSrc (fn){
const src = fn.toString();
return src.substring(src.indexOf('{')+1,src.lastIndexOf('}')-1);
}
function fnArgs (fn){
let src = fn.toString();
src = src.substr(src.indexOf('('));
src = src.substr(0,src.indexOf(')')-1);
src = src.substr(1,src.length-2);
return src.split(',');
}
function runSanitizedFunctionInSandbox(fn,forbidden) {
const playground = shim(window,forbidden);
playground.window = playground;
let sandboxed_code = fn.bind(playground,playground.window);
sandboxed_code();
}
function runFunctionInSandbox(fn,forbidden) {
const src = fnSrc(fn);
const args = fnArgs(fn);
executeSandboxedScript(src,args,forbidden);
}
function executeSandboxedScript(sourceCode,arg_names,forbidden) {
var script = document.createElement("script");
script.onload = script.onerror = function(){ this.remove(); };
script.src = "data:text/plain;base64," + btoa(
[
'runSanitizedFunctionInSandbox(function(',
arg_names,
['window'].concat(forbidden),
'){ ',
sourceCode,
'},'+JSON.stringify(forbidden)+')'
].join('\n')
);
document.body.appendChild(script);
}
<span id="myspan">Page Access IS OK<span>
or a slightly more involved version that allows arguments to be passed to the function
function suspectCode(argument1,argument2) {
console.log (window.document.querySelector("#myspan").innerText);
console.log(argument1,argument2);
console.log('verbotten_data:',typeof verbotten_data==='string'?verbotten_data:'<BANNED>');
console.log('secret_data:',typeof secret_data==='string'?secret_data:'<BANNED>'); // undefined === we can't
console.log('window.secret_data:',typeof window.secret_data==='string'?window.secret_data:'<BANNED>');
secret_data = 'i changed the secret data !';
console.log('secret_data:',typeof secret_data==='string'?secret_data:'<BANNED>'); // undefined === we can't
console.log('window.secret_data:',typeof window.secret_data==='string'?window.secret_data:'<BANNED>');
}
var verbotten_data = 'a special secret';
window.secret_data = 'special secret.data';
console.log("first call the function directly");
suspectCode('hello','world') ;
console.log("now run it in a sandbox, which banns 'verbotten_data' and 'secret_data'");
runFunctionInSandbox (suspectCode,['hello','sandboxed-world'],
[
'verbotten_data','secret_data',
// we can't touch items tied to stack overflows' domain anyway so don't clone it
'sessionStorage','localStorage','caches',
// we don't want the suspect code to beable to run any other suspect code using this method.
'runFunctionInSandbox','runSanitizedFunctionInSandbox','executeSandboxedScript','shim',
]);
function shim(obj,forbidden) {
const valid=Object.keys(obj).filter(function(key){return forbidden.indexOf(key)<0;});
var shimmed = {};
valid.forEach(function(key){
try {
shimmed[key]=obj[key];
} catch(e){
console.log("skipping:",key);
}
});
return shimmed;
}
function fnSrc (fn){
const src = fn.toString();
return src.substring(src.indexOf('{')+1,src.lastIndexOf('}')-1);
}
function fnArgs (fn){
let src = fn.toString();
src = src.substr(src.indexOf('('));
src = src.substr(0,src.indexOf(')'));
src = src.substr(1,src.length);
return src.split(',');
}
function runSanitizedFunctionInSandbox(fn,args,forbidden) {
const playground = shim(window,forbidden);
playground.window = playground;
let sandboxed_code = fn.bind(playground,playground.window);
sandboxed_code.apply(this,new Array(forbidden.length).concat(args));
}
function runFunctionInSandbox(fn,args,forbidden) {
const src = fnSrc(fn);
const arg_names = fnArgs(fn);
executeSandboxedScript(src,args,arg_names,forbidden);
}
function executeSandboxedScript(sourceCode,args,arg_names,forbidden) {
var script = document.createElement("script");
script.onload = script.onerror = function(){ this.remove(); };
let id = "exec"+Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString();
window.execArgs=window.execArgs||{};
window.execArgs[id]=args;
let script_src = [
'runSanitizedFunctionInSandbox(function(',
['window'].concat(forbidden),
(arg_names.length===0?'':','+arg_names.join(","))+'){',
sourceCode,
'},',
'window.execArgs["'+id+'"],',
JSON.stringify(forbidden)+');',
'delete window.execArgs["'+id+'"];'
].join('\n');
let script_b64 = btoa(script_src);
script.src = "data:text/plain;base64," +script_b64;
document.body.appendChild(script);
}
<span id="myspan">hello computer...</span>
I verified #josh3736's answer but he didn't leave an example
Here's one to verify it works
parent.html
<h1>parent</h1>
<script>
abc = 'parent';
function foo() {
console.log('parent foo: abc = ', abc);
}
</script>
<iframe></iframe>
<script>
const iframe = document.querySelector('iframe');
iframe.addEventListener('load', function() {
console.log('-calling from parent-');
iframe.contentWindow.foo();
});
iframe.src = 'child.html';
</script>
child.html
<h1>
child
</h1>
<script>
abc = 'child';
function foo() {
console.log('child foo: abc = ', abc);
}
console.log('-calling from child-');
parent.foo();
</script>
When run it prints
-calling from child-
parent foo: abc = parent
-calling from parent-
child foo: abc = child
Both child and parent have a variable abc and a function foo.
When the child calls into the parent's foo that function in the parent sees the parent's global variables and when the parent calls the child's foo that function sees the child's global variables.
This also works for eval.
parent.html
<h1>parent</h1>
<iframe></iframe>
<script>
const iframe = document.querySelector('iframe');
iframe.addEventListener('load', function() {
console.log('-call from parent-');
const fn = iframe.contentWindow.makeFn(`(
function() {
return abc;
}
)`);
console.log('from fn:', fn());
});
iframe.src = 'child.html';
</script>
child.html
<h1>
child
</h1>
<script>
abc = 'child';
function makeFn(s) {
return eval(s);
}
</script>
When run it prints
-call from parent-
from fn: child
showing that it saw the child's abc variable not the parent's
note: if you create iframes programmatically they seem to have to be added to the DOM or else they won't load. So for example
function loadIFrame(src) {
return new Promise((resolve) => {
const iframe = document.createElement('iframe');
iframe.addEventListener('load', resolve);
iframe.src = src;
iframe.style.display = 'none';
document.body.appendChild(iframe); // iframes don't load if not in the document?!?!
});
}
Of course in the child above we saw that the child can reach into the parent so this code is NOT SANDBOXED. You'd probably have to add some stuff to hide the various ways to access the parent if you want make sure the child can't get back but at least as a start you can apparently use this technique to give code a different global scope.
Also note that of course the iframes must be in the same domain as the parent.
Here's another answer. This one's based on how chained scopes work in javascript. It also uses Function(), which produces faster code than eval.
/** This takes a string 'expr', e.g. 'Math.max(x,1)' and returns a function (x,y)=>Math.max(x,1).
* It protects against malicious input strings by making it so that, for the function,
* (1) no names are in scope other than the parameters 'x' and 'y' and a whitelist
* of other names like 'Math' and 'Array'; (2) also 'this' binds to the empty object {}.
* I don't think there's any way for malicious strings to have any effect.
* Warning: if you add something into the global scope after calling make_fn but
* before executing the function you get back, it won't protect that new thing.
*/
function make_fn(expr) {
const whitelist = ['Math', 'Array']; // Warning: not 'Function'
let scope = {};
for (let obj = this; obj; obj = Object.getPrototypeOf(obj)) {
Object.getOwnPropertyNames(obj).forEach(name => scope[name] = undefined);
}
whitelist.forEach(name => scope[name] = this[name]);
const fn = Function("scope","x","y","with (scope) return "+expr).bind({});
return (x,y) => fn(scope,x,y);
}
This is how it behaves: https://jsfiddle.net/rkq5otme/
make_fn("x+y")(3,5) ==> 8
make_fn("Math.max(x,y)")(3,5) ==> 5
make_fn("this")(3,5) ==> {}
make_fn("alert('oops')") ==> TypeError: alert is not a function
make_fn("trace((function(){return this}()))")(3,5) ==> ReferenceError: trace is not defined
Explanation. Consider a simpler version
Function("x", "return Math.max(x, window, this);")
This creates a function with the specified body which has two chained scopes: (1) the function scope which binds x, (2) the global scope. Let's spell out how the symbols Math, x, window and this are all resolved.
x is bound to the property in the function scope
window is bound to the property in the next chained scope, global, i.e. window['window'].
We don't want this! To prevent it, we create our own scope object scope = {window:undefined,...} and write with (scope) return Math.max(x,window,this). The with statement is only allowed in non-strict code. It adds an additional scope, so the chain is now: (0) the specified scope object we created, (1) the function scope which binds x, (2) the global scope. Because name 'window' is found in our scope object, it can never bind to the one in global scope.
Math will suffer the same fate.
We want to whitelist it! So we make our scope object {Math:global.Math, window:undefined, ...}
this is bound upon invocation of the function to the global object, window.
We don't want this! To prevent it, we call .bind({}) on the function, which wraps the function in a wrapper which sets this={}. Alas bind(null) didn't seem to bind.
Note: there's another possible construction of the scope object, using Proxy. It doesn't seem particularly better.
const scope = new Proxy({}, {
has: (obj, key) => !['x','y'].includes(key),
get: (obj, key) => (whitelist.includes(key)) ? this[key] : undefined,
set: (obj, key, value) => {},
deleteProperty: (obj, key) => {},
enumerate: (obj, key) => [],
ownKeys: (obj, key) => [],
defineProperty: (obj, key, desc) => {},
getOwnPropertyDescriptor: (obj, key) => undefined,
});

Executing Javascript functions by reference

I'm wondering if any of yall have any insight as to how one could execute a function by reference in javascript.
http://mootools.net/shell/yL93N/1/
Any discussion would be cool.
-Chase
looking at your mooshell, the way i'd handle it in mootools is this:
http://mootools.net/shell/yL93N/10/
var proxyFunction = new Class({
message: "hello",
Binds: ['passByReference','sayit'],
passByReference: function(func) {
// console.log(this, this[func]);
if (this[func] && $type(this[func]) === "function")
this[func]();
},
sayit: function() {
alert(this.message);
},
killit: function() {
document.write('we\'re dead');
}
});
$('tryit').addEvent('change',function(e){
new proxyFunction().passByReference(this.get('value'));
});
// or have a permanent proxy instance if you call methods of the class often and need it to change things.
var proxy = new proxyFunction();
$('tryit').addEvent('change',function(e){
proxy.passByReference(this.get('value'));
});
the advantage of doing so is that all your proxied functions are behind a common object, don't pollute your window namespace as global variables and can share data that relates to the event.
Not exactly sure what you mean, but you can do this:
var func = window.alert;
var args = ["hello world"]
func.apply(window, args)
Globally-defined functions (and variables) are visible as members of the global window object.
Members of an object can be fetched by name using the square bracket notation: o['k'] is the same as o.k. So, for your example:
var function_name= $(this).val();
window[function_name]();
Like this?
function blah() {
...do stuff
}
myref = blah
myref()
The best way is to do:
func.call();
Function variables in JavaScript already are references. If you have a function:
var explode = function() { alert('boom!'); };
You can pass explode around as an argument, and it's only passing a handle to that function, not the entire function body.
For proof of this, try:
explode.id = 5;
var detonate = explode;
alert(detonate.id); // => 5
explode.id = 6;
alert(detonate.id); // => 6
functions are first class objects in Java Script. Effectively this means that you can treat it very much as if it were a variable, and pass it anywhere that you would expect a variable.
e.g.
var myFn = function() { alert('inside anonymous fn'); }
function callMyFn(paramFn)
{
paramFn();
}
callMyFn(myFn); //inside anonymous fn
function MyFnHolders(argFn)
{
this.argFn = argFn;
this.fieldFn = function() {
alert('inside fn field');
}
}
var myFnHolders = new MyFnHolders(myFn);
myFnHolders.argFn(); //'inside anonymous fn'
myFnHolders.fieldFn(); //'inside fn field'
//etc
so passing a function by ref can be done simply by assigning it to a variable and passing it around.
Here's one with a closure for your arguments...
function Runner(func, args) {
return function() { return func.apply(window, args); };
}
var ref = new Runner(window.alert, ["hello world"]);
ref();

Categories