I have a server side Python script that returns a JSON string containing parameters for a client side JavaScript.
# Python
import simplejson as json
def server_script()
params = {'formatting_function': 'foobarfun'}
return json.dumps(params)
This foobarfun should refer to a JavaScript function. Here is my main client side script
// JavaScript
function client_script() {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, async=true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
options = JSON.parse(xhr.responseText);
options.formatting_function();
}
};
xhr.send(null);
}
function foobarfun() {
//do_something_funny_here...
}
Of course, options.formatting_function() will complain that "a string is not callable" or something to that effect.
Upon using Chrome's Inspect Element, under the Resources tab, and navigating the left sidebar for XHR > query, I find that client_script interprets options as below. foobarfun is seen as a string.
// JavaScript
options = {"formatting_function": "foobarfun"}
I would have liked client_script to see options as
// JavaScript
options = {"formatting function": foobarfun}
Of course, doing the following within Python will have it complaining that it doesn't know anything about foobarfun
# Python
params = {'formatting_function': foobarfun}
QUESTION:
How should I prepare my JSON string from the server side so that the client script can interpret it correctly? In this case, I want foobarfun to be interpreted as a function object, not as a string.
Or maybe it's something I should do on the client side?
There's nothing you can do in the JSON to get the result you want because JSON has no concept of functions, it's purely a data notation. But there are things you can do client-side.
If your foobarfun function is a global function (which I would recommend against, we'll come to that), then you can call it like this:
window[options.formatting_function]();
That works because global functions are properties of the window object, and you can access properties either by using dotted notation and literals (window.foobarfun), or by using bracketed notation and strings (window["foobarfun"]). In the latter case, of course, the string doesn't have to be a string literal, it can be a string from a property -- your options.formatting_function property, for instance.
But I don't recommend using global functions, the window object is already very crowded. Instead, I keep all of my functions (or as many as possible, in some edge cases) within a master scoping function so I don't add anything to the global namespace:
(function() {
function foobarfun() {
}
})();
Now, if you do that, you can't access foobarfun on window because the whole point of doing it is to avoid having it be on window. Instead, you can create your own object and make it a property of that:
(function() {
var myStuff = {};
myStuff.foobarfun = foobarfun;
function foobarfun() {
}
function client_script() {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, async=true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
options = JSON.parse(xhr.responseText);
myStuff[options.formatting_function](); // <== using it
}
};
xhr.send(null);
}
})();
Frequently, rather than this:
myStuff.foobarfun = foobarfun;
function foobarfun() {
}
you'll see people write:
myStuff.foobarfun = function() {
};
I don't recommend that, either, because then your function is anonymous (the property on myStuff that refers to the function has a name, but the function doesn't). Giving your functions names is a good thing, it helps your tools help you (showing you the names in call stacks, error messages, etc.).
You might also see:
myStuff.foobarfun = function foobarfun() {
};
and that should be valid, it's correct JavaScript. But unfortunately, various JavaScript implementations have various bugs around that (which is called a named function expression), most especially Internet Explorer prior to IE9, which will create two completely different functions at two different times.
All of that said, passing the names of functions around between the client and server usually suggests that you want to step back and look at the design again. Data should drive logic, but not in quite such a literal way. That said, though, there are definitely valid use cases for doing this, you may well have one in your situation.
This question seems like it may be helpful for you:
How to execute a JavaScript function when I have its name as a string
I think what I would do is store references to these methods in an object literal, and then access them through properties.
For example, if I wanted to call foobarfun, among other functions
var my_functions = {
foobarfun: function(){
},
...
};
...
var my_fn = my_functions[options.formatting_function];
my_fn();
may be you can think the return string as javascript not json, by set MIME type text/javascript
There is a trick you can do. Analyzing the code of the json module in python, I notice that it is possible to make the serializer believe that it is an int what is serializing that way __str__ method will be executed.
import json
class RawJS(int):
def __init__(self):
self.code = "null"
def __repr__(self):
return self.code
def __str__(self):
return self.__repr__()
#classmethod
def create(cls, code):
o = RawJS()
o.code = code
return o
js = RawJS.create("""function() {
alert("hello world!");
}""")
x = {
"func": js,
"other": 10
}
print json.dumps(x)
the output is:
{"other": 10, "func": function() {
alert("hello world!");
}}
The disadvantage of this method is that the output is not a valid JSON in python so it can't be deserialized, but it is a valid javascript.
I don't know whether it's possible to trick Python's JSON serializer to do what you want, but you can use eval() function to execute any JavaScript code stored in a string variable.
Well I am not a Python expert but I know some java script. From server you can pass the information to client only as string. If in any case you want to pass any java script function then you can pass that function name as string and evaluate that string on client side.
Ex. If you passes xyz as string from server and on client side you call
var funcName = "xyz"; // pursuing that variable would pass here some how
eval (funcName); // This line would make a call to java script function xyz
Hint: You can think of java script eval utility as a reflection utility in java.
Thanks
shaILU
I'm in the same situation (python backend, needing to pass a JS function through JSON to frontend).
And while the answers here are honestly mind-blowing, I'm asking myself whether passing a javascript function that needs to designed in python and snuck into a JSON is the right answer, from a design perspective.
My specific scenario I need to tell JS how to format a string (is it a percentage, or does it need unitary breakdown such as thousands, millions etc)
And I'm finding it's cleaner to just do:
python:
chart = {
y_axis: {
"formatter" : "percent"
}
}
JS:
format_type = chart["y_axis"]["formatter"]
switch(format_type) {
case "percent":
format_func = (value) => value.toFixed(0) +'%';
break;
}
chart["y_axis"]["formatter"] = format_func
I find this is overall cleaner than attempting to define your JS functions in Python. It's also more decoupled than passing a specific function name from python to JS
Which I guess it's quite similar to #jkeesh's solution
Related
I know that converting a buffer to a json object is trivial with:
JSON.parse(buffer)
But what about converting a buffer that contains a javascript object with functions to a real javascript object ??
I have files that contact objects with functions and I need to read these files in and save them to another master object.
While the task of reading the files is not a problem, I cannot find any way of converting the buffer back into a real javascript object again.
This is a very simple example of a file 'test.js' that I am trying to load
{
get:function(){return 'hello'},
somevar: "xxx",
put: function(){return 'world'}
}
Reading this data in it is a buffer, I can't convert using JSON as this contains functions and I cannot read using utf8 encoding as it will become a string !
var funcs = {}
fs.readFile('test.js',function(err,buff){
funcs['test'] = buff;
})
Is it possible to read a file and convert it into a real javascript object ?
Edit
OK, I have found a solution but it is using the dreaded eval(), however this is backend code and as far as I can tell there's no way for anything to be injected by a user from the frontend, I would prefer not to use it but unless there's anything that will work without modifying the format of the files, I will use it:
var funcs = {}
fs.readFile('test.js','utf8',function(err,buff){
eval('var_='+buff);
funcs['test'] = _;
})
For what it's worth, you could use the Function constructor. It's slightly safer than eval because it doesn't access the local scope. But it can still access global variables.
var script = buffer.toString('utf8'); // assuming the file is in UTF-8
var returnObject = new Function('return ' + script);
var myObject = returnObject();
Depending on the complexity of the code you're dealing with, this approach may or may not suit your needs:
You can create a temporary file, say "test-module.js", which includes the object from "test.js" but with a module export prependend, for example:
module.exports = {
get: function() { return 'hello' },
somevar: 'xxx',
put: function() { return 'world' }
}
Then, from your main file, you can retrieve the file content already available as a Javascript object:
var funcs = {}
var buff = require('./test-module');
funcs['test'] = [buff.get, buff.put];
Would this solution work for you?
I'm building a simple node.js websocket server and I want to be able to send a request from a client to the server and have it just take care of things (nothing that could cause harm). Ideally the client will pass the server an object with 2 variables, one of them for the object and the other for the specific function in that object to call. Something like this:
var callObject = {
'obj': 'testObject',
'func':'testFunc'
}
var testObject = {
func: function(){
alert('it worked');
}
}
// I would expect to be able to call it with sometihng like.
console.log( window[callObject.obj] );
console.log( window[callObject.obj][callObject.func] );
I tried calling it with global (since node.js doesn't uses it instead of a browsers window) but it won't work, it always tells me that it can't find callObject.func of undefined. If I call a console.log on callObject.obj it shows the objects variable, as a string, as expected. If run a console.log on the object itself I get the object back.
I'm guessing this is something rather simple, but my Google-fu has failed me.
My recommendation is to resist that pattern and not have client code pick any function to call. If you are not careful you have built yourself a nice large security hole. Especially if you are considering using eval.
Instead have a more explicit mapping between data sent by the client and server code. (Similar to what routes in express what give you).
You might have something like this
const commands = { doSomething() { ... } );
// Then you should be able to say:
let clientCommand = 'doSomething'; // from client
commands[clientCommand](param);
This should be pretty close to what you want to achieve.
Just make sure doSomething validates any parameters passed in.
For two levels of indirection:
const commandMap = { room: { join() { ...} }, chat: { add() { ... } }};
// note this is ES6 syntax
let clientCmd = 'room';
let clientFn = 'join';
commandMap[clientCmd][clientFn]();
I think you might just have to find the right place to put the command map. Show your web socket handler code.
Probably many of you tried to achieve encapsulation in JavaScript. The two methods known to me are:
a bit more common I guess:
var myClass(){
var prv //and all private stuff here
//and we don't use protoype, everything is created inside scope
return {publicFunc:sth};
}
and second one:
var myClass2(){
var prv={private stuff here}
Object.defineProperty(this,'prv',{value:prv})
return {publicFunc:this.someFunc.bind(this)};
}
myClass2.prototype={
get prv(){throw 'class must be created using new keyword'},
someFunc:function(){
console.log(this.prv);
}
}
Object.freeze(myClass)
Object.freeze(myClass.prototype)
So, as second option is WAY more convenient to me (specifically in my case as it visually separates construction from workflow) the question is - are there any serious disadvantages / leaks in this case? I know it allows external code to access arguments of someFunc by
myClass.protoype.someFunc.arguments
but only in case of sloppily executed callbacks (synchronously inside caller chain). Calling them with setTimeout(cb,0) breaks chain and disallows to get arguments as well as just returning value synchronously. At least as far as i know.
Did I miss anything? It's kind of important as code will be used by external, untrusted user provided code.
I like to wrap my prototypes in a module which returns the object, this way you can use the module's scope for any private variables, protecting consumers of your object from accidentally messing with your private properties.
var MyObject = (function (dependency) {
// private (static) variables
var priv1, priv2;
// constructor
var module = function () {
// ...
};
// public interfaces
module.prototype.publicInterface1 = function () {
};
module.prototype.publicInterface2 = function () {
};
// return the object definition
return module;
})(dependency);
Then in some other file you can use it like normal:
obj = new MyObject();
Any more 'protecting' of your object is a little overkill for JavaScript imo. If someone wants to extend your object then they probably know what they're doing and you should let them!
As redbmk points out if you need private instance variables you could use a map with some unique identifier of the object as the key.
So, as second option is WAY more convenient to me (specifically in my case as it visually separates construction from workflow) the question is - are there any serious disadvantages / leaks in this case?
Hm, it doesn't really use the prototype. There's no reason to "encapsulate" anything here, as the prototype methods will only be able to use public properties - just like your untrusted code can access them. A simple
function myClass2(){
var prv = // private stuff here
Object.defineProperty(this, 'prv', {value:prv})
// optionally bind the publicFunc if you need to
}
myClass2.prototype.publicFunc = function(){
console.log(this.prv);
};
should suffice. Or you use the factory pattern, without any prototypes:
function myClass2(){
var prv = // private stuff here
return {
prv: prv,
publicFunc: function(){
console.log(this.prv); // or even just `prv`?
}
};
}
I know it allows external code to access arguments of someFunc by
myClass.protoype.someFunc.arguments
Simply use strict mode, this "feature" is disallowed there.
It's kind of important as code will be used by external, untrusted user provided code.
They will always get your secrets if the code is running in the same environment. Always. You might want to try WebWorkers instead, but notice that they're still CORS-privileged.
To enforcing encapsulation in a language that doesn't properly support private, protected and public class members I say "Meh."
I like the cleanliness of the Foo.prototype = { ... }; syntax. Making methods public also allows you to unit test all the methods in your "class". On top of that, I just simply don't trust JavaScript from a security standpoint. Always have security measures on the server protecting your system.
Go for "ease of programming and testing" and "cleanliness of code." Make it easy to write and maintain, so whichever you feel is easier to write and maintain is the answer.
I have module pattern done like this:
var A = (function(x) {
var methodA = function() { ... }
var methodB = function() { ... }
var methodC = function() { ... }
...
...
return {
methA: methodA,
methB: methodB
}
})(window)
This code let's me call only methA and methB() on A which is what I want and what I like. Now the problem I have - I want to unit test it with no pain ot at least with minimal efforts.
First I though I can simply return this but I was wrong. It returns window object.(can someone explain why?).
Second - I found solution somewhere online - to include this method inside my return block:
__exec: function() {
var re = /(\(\))$/,
args = [].slice.call(arguments),
name = args.shift(),
is_method = re.test(name),
name = name.replace(re, ''),
target = eval(name);
return is_method ? target.apply(this, args) : target;
}
This method let's me call the methods like this: A.__exec('methA', arguments);
It is almost what I want, but quite ugly. I would prefer A.test.methA() where test would never be used in production code - just for revealing private methods.
EDIT
I see people telling me to test the big thing instead of the small parts. Let me explain. In my opinion API should reveal only the needed methods not a bunch of internal functions. The internals because of their small size and limited functionality are much easier to test then test the whole thing and guess which part gone wrong.
While I may be wrong, I would still like to see how I could return references to all the methods from the object itself :).
Answer to your first question(you return this, but it returns window, not the object you wanted): in javascript this inside the function returns global object unless this function is a method of the object.
Consider next examples:
1) this points to the global object ():
function(){
return this;
}
2) this points to the object:
var obj = {
value: "foo",
getThisObject: function(){
return this;
}
}
Your case is example #1, because you have a function, that returns an object. This function is not a method of any object.
The best answer to your second question is to test only public methods, but if
that is so important for you, I can propose next:
create your modules dynamically on server side.
How it works:
create separate scripts for functionality you want;
create tests for these separate scripts;
create method that will combine scripts into one however you want;
to load script, reference to the combining scripts method.
Hopefully, it can solve your problem. Good luck!
Why not use namespaces to add your modules and public methods to js engine. Like this:
window['MyApp']['MODULE1'] = { "METHOD1" : {}, "METHOD2" : {}};
I write modules like this Sample module in JavaScript.
And test it like this: Simple unit testing in JavaScript
The use of eval() is generally not good idea.
I had a json parsing problem updating my app from prototype 1.6.1 to 1.7.0
This is a very simplified model of my json as it, saved in tmp.js:
{
"text":"hello world",
"fn": function(){alert('hello world')
}
and this is my code:
new Ajax.Request('tmp.js', {
onSuccess: function(transport){
var json = transport.responseText.evalJSON();
var button = new Element('button')
.update(json.text)
.observe('click', function(){
json.fn();
});
$('my_div').update(button);
}});
All this worked correctly with 1.6.1: it produced a button that alerted 'hello world' on click.
This does not work in v. 1.7.0, because of the fact that my json is not valid. I know it should not contain functions, but only data.
My question is: why did it worked with 1.6.1 (and still works) and is there a way to accomplish the same with 1.7.0. I need to get via ajax a js object containing user defined functions.
Thank you
Update:
The reconstruct function is a good solution and I think I'll use it in the future.
Anyway I found eval() function that seems to be a good and fast solution:
tp.js JSON:
{
"text":"hello world",
"fn": "my_alert('hello world')"
}
JS
function my_alert(string){
alert(string);
}
new Ajax.Request('tmp.js', {
onSuccess: function(transport){
var json = transport.responseText.evalJSON();
var button = new Element('button')
.update(json.text)
.observe('click', function(){
eval(json.fn);
});
$('my_div').update(button);
}});
What you've got in that sample data you posted is not JSON. In strict JSON the value of a property can be
a string
a number
boolean true or false
null
an array
an object
There is no way to include a function definition in JSON. (Well, that's not exactly true; you're free to use strings, numbers, arrays, objects, etc. to describe a function in such a way that your code can reconstruct it after the JSON is parsed. The point is that straight JavaScript function expressions are disallowed.)
One simple, slightly disturbing thing you could do is save the function body as a string, and then reconstruct it by calling
foo.fn = new Function(foo.fn);
once the JSON parse is complete.
edit more details:
The "Function()" constructor takes as its arguments a list of strings representing argument names, followed by a string that's to be used as the function body. If you wanted to encode a complete JavaScript function, therefore, you might want to have it look like an object:
{
'foo': 'plain property',
'someFunction': {
'arguments': [ 'x', 'y' ],
'body': 'if (x > y) return "x"; return "y";'
}
}
Now to turn "someFunction" into a real function, you'd use something like this:
function reconstructFunction(descr) {
var params = (descr.arguments || []).slice(0);
params.push(descr.body);
return Function.apply(null, params);
}
Then you can just pass the function descriptor from your JSON into something like that, and then you have a bona fide JavaScript function to call.