Getting object name within the objectscope - javascript

I have a Javascript class that contains
add
remove
removeall
update
.
.
.
updateLik
.
.
functions.
And in my Serverside script a have Links like
Add
Now if user click on the DIV, the function "add" will be called.
and the add function calls in turn the updateLink function.
updateLink replaces the onclick attribute to "myobject.delete(100)"
Now my problem is that in updateLink function i had to hardcode the objectname
to call its delete function.
Is there any way to get the objectname, or any other solution?
Thanks

You could store a reference of the context where your object is created, and then search within it, looking for the actual instance:
function MyClass() {
this.getVarName = function () {
for (var name in this.scope)
if (this.scope[name] === this)
return name;
}
}
MyClass.prototype.scope = this;
var myObject = new MyClass();
myObject.getVarName(); // returns "myObject"

Simplest way is to use eval.
var myObject = eval(myObjectName);
That is if you have the name of the object in a string format.
I would also look at at the YUI event library. It allows you to execute any function in any scope.
http://developer.yahoo.com/yui/event/

Example:
var x = new YourClass();
var y = x;
Question: What is the name of your object? x or y?
Solution:
var x = new YourClass('x'); // and have your class copy the name
or
var x = new YourClass();
x.name = 'x';

delete is a keyword so you cannot use it with dot-notation (object.method). You have to use ['property']-notation (object['method']).
var obj = {delete: function(){}}; // throws a syntax error
var obj = {'delete': function(){}}; // works
obj.delete() // throws a syntax error
obj['delete']() // works

Related

How to convert a string into function in javascript?

I converted a function into string using stringify and stored into database.
But how to add this string as function to a variable
lets assume i get string like i am showing in A
var A = "function aF() {\n console.log(\'change1\');\n}"
I want to add aF function to a object key like this
{handle: A }
But i am getting this in result
{ handle: 'function aF() {\n console.log(\'change1\');\n }' }
instead i want this
{handle:[function: aF]} or {handle:[function]}
Because variable A is typeof string. Is there any way to convert A into function and then store into handle key.
You can use Function constructor to make a functions.
For Example.
var A = "function aF() {\n console.log(\'change1\');\n}" ;
var functionStr = A.substring(A.indexOf("{")+1, A.lastIndexOf("}"));
new Function(functionStr)();
Note:
Using a string to create function object with this method is as risky as eval(). You should not do it unless you are sure that user-input is not involved. If user-input is used in making a function string then function is not considered secure as user can potentially manipulate around the authentication and authorization since system cannot control (validate) the same.
what if function aF have some parameter
You need to store the reference to the function object and invoke the same with parameter, for example
var A = "function aF() {\n console.log(\'change1\');\n}" ;
var functionStr = A.substring(A.indexOf("{")+1, A.lastIndexOf("}"));
var functionObj = new Function(functionStr);
Now invoke this function with parameter, for example
functionObj ( args );
or use call
functionObj.call( this, args );//this is the context you want to bind to this funciton.
or use apply
functionObj.apply( this, args );//this is the context you want to bind to this funciton.
Heres how I would do it, this is pretty much another take on other responses but doesnt involve any sub-strings. The comments on the code pretty much say it all.
var yourExample = createFunction("function aF() {\n console.log(\'change1\');\n}");
yourExample(); // prints change1 in console
var fnWithParam = createFunction("function aF(param1) { console.log(param1); }");
fnWithParam(2); // prints 2 in console
// creates a function from a string, that string must be a function itself.
function createFunction(fnStr) {
// make a function which returns a function, unwrap that function by calling it with apply();
return new Function('return ' + fnStr).apply();
}
Also to help reduce your exposure to access to objects such as window or document you can create new variables inside the function scope which creates that function. For instance:
// creates a function from a string, that string must be a function itself.
function createFunction(fnStr) {
// make a function which returns a function, unwrap that function by calling it with apply();
return new Function('"use strict"; var window,document; return ' + fnStr).apply();
}
Now this doesnt solve all the security issues around creating javascript from strings but I think its better than nothing.
Good readings:
"use strict"; - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
new Function() - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
An other solution would be to do :
var A = "function aF() {\n console.log(\'change1\');\n}" ;
eval(A);
{handle:aF}
Good day.
I wrote small function.
It is very simple, without many validations.
See here
function makeFooFromString(str){
var m = str.match(/function\s+([^(]+?)\s*\(([^)]*)\)\s*{(.*)}/);
// function name
var fooname = m[1];
// function params
var params = m[2];
// function body
var body = m[3];
// processing params
if(params.length){
params = params.split(',');
for(var i = 0; i < params.length; i++){
params[i] = params[i].replace(/\s*/, '');
}
}else{
params = [];
}
params = params.join(',');
// make our temp function body
var text = '';
text += 'var foo = function('+params+'){'+body+'};';
text += 'return foo.apply(foo, arguments);';
return new Function(text);
};
Now i make call like this
var foo = makeFooFromString('function get_sum(a, b){ return a+b;}')
And test
console.log(foo);
console.log(foo(1, 2));
console.log(foo(3, 4));
See it on jsfiddle
https://jsfiddle.net/j586xajq/

TypeError: handlers.push is not a function

How to fix this issue?
// Add to the element's handler list, delegates in front
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
}
push() is an operation off of an array object such as:
var x = [];
var y = new Array();
x.push("me");
y.push("you");
To tell if a variable is an array you can do console.log(typeof [variable]); to see what type it is, or alternatively just console log the variable to see the contents.
If you are not using an array, but are using a javascript object, you should add elements to it via basic assignment. Example:
var x = {}; //this works as an object
x.myNewVariable = "My new value";
After which you will be able to use and access that variable off of the object. If you later want to remove that you can do so by:
delete x.myNewVariable;

Unexpected behavior using getters and setters

Look this code:
<script>
function dbg (object) {
var _string = "";
for (var a in object) {
_string += a + ":\n";
for (var b in object[a])
if (/^get_/.test (b))
_string += "\t" + b + " - " + object[a][b] () + "\n";
}
return _string;
}
function Order () {
var products = [];
this.get_products = function () {return products;}
this.set_products = function (_products) {products = _products;}
}
function Product () {
var id = null;
var name = null;
this.get_id = function () {return id;}
this.get_name = function () {return name;}
this.set_id = function (_id) {id = _id;}
this.set_name = function (_name) {name = _name}
}
var order = new Order ();
var product = new Product ();
product.set_id (1);
product.set_name ("Banana");
order.set_products (order.get_products ().concat (product));
alert (dbg (order.get_products ())); // Ok
product.set_id (2);
product.set_name ("Orange");
order.set_products (order.get_products ().concat (product));
alert (dbg (order.get_products ())); // Duplicated values! What?
</script>
The first time you push the object "Product" into the object "Order", everything looks fine.
When you set new values to the object "Product", the object itself overwrites the previous values of the object "Order". The final result is a array of duplicated values. Is it normal ? Is there a workaround? Just tried everything I knew without success. Thanks.
Crazy Train has already answered it in the comments. The question is listed having 0 answers so I'll add it as an answer.
When adding a variable containing an object to an array you add a reference to the variable, when you re assign the variable the reference is broken.
Adding a variable containing an object to an array then re assigning the variable doesn't change the object in the array:
var arr=[];
var object={name:"John"};
arr.push(object);
object=33;
console.log(arr);//=[Object {name="john"}]
Adding a variable containing an object to an array then changing the internal values of the object that the variable contains does change the object in the array:
var arr=[];
var object={name:"John"};
arr.push(object);
object.name="Jane";
console.log(arr);//=[Object {name="Jane"}]
So to correct your code you could do the following:
Create a new variable for the product to be added:
var product2=new Product();
product2.set_id (2);
product2.set_name ("Orange");
order.set_products (order.get_products ().concat (product2));
Or break the reference between your product variable and the products array in order:
product=null;//product has no ref to order.products
product=new Product();
product.set_id (2);
product.set_name ("Orange");
order.set_products (order.get_products ().concat (product));
I would not define members of an object in a constructor function with var as JavaScript doesn't support private members. You can simulate them by creating closures but that has it's own problem when you have instance specific privates (as is your case). You can't use prototype if the functions need to access private instance variables, you can't clone it unless you have public accesssors, inheritance and overriding functions will be a pain.
Here is some more info on using constructor functions.
If you have Chrome or Firefox (with Firebug) then you can press F12 to open the console. You an detach the console window (have it's own window) then copy code in the before mentioned answers and paste them in the commandline of the console. There you can run and re run the code, change and see the output to better understand JS behavior.
You are just overriding the variables in object. I'd do it like this, much simpler:
var products = {
set : function(name,id) {
products.list.push({name:name,id:id});
},
get : function(id) {
var r;
if(typeof id === 'number'){
products.list.forEach(function(e,i){ if(e.id==id) r= products.list[i];});
} else {
products.list.forEach(function(e,i){ if(e.name==id) r = products.list[i];});
}
return r;
},
list : []
};
var order={
set : function(p) {
order.list[p.id]=p;
},
get : function(id) {
return order.list[id];
},
delete : function(id) {
return delete order.list[id];
},
list : {}
};
then you can do this
products.set('apple',34);
products.set('orange',4);
products.set('mango',1);
var x = products.get(1);
var y = products.get('orange');
order.set(x);
order.set(y);
working demo:
http://jsfiddle.net/techsin/tjDVv/2/

Adding a property to a "Class" in JavaScript

There are no actual classes in javascript. But you have to work with what you get.
Lets take this example "Class":
var example = function (string) {
this._self = string;
}
With the above, you could do something like:
var ex = new example("Hello People."),
display = ex._self; // returns "Hello People."
I thought that by using something like example.prototype.newFun = function(){} would add a new property to that "Class". But it isn't working in my code.
Here is the full code i'm testing:
var example = function (string) {
this._self = string;//public, var like, storage
}
var showExample = new example("Hello People");
showExample.prototype.display = function (a) {//code stops here, with error "Uncaught TypeError: Cannot set property 'display' of undefined"
return a;
}
console.log(showExample._self);
console.log(showExample.display("Bye"));
What i'm trying to do is add the display function to the example function as a "public function". I might be doing something wrong.
It's not the object that has the prototype, it's the function that you use to create the object:
var example = function (string) {
this._self = string;
}
example.prototype.display = function (a) {
return a;
};
Because there's no prototype for showExample - it's only an instance of example. Try to do this: example.prototype.display = function (a) {} and it will work.
Here's a bit more on classes in JavaScript:
3 Ways to "define" classes
This lovely SO question
I like the way Classy handles this and also how classes are implemented in CoffeeScript.
You can modify to the constructor of showExample ..
ex.
showExample.constructor.prototype.display = function (a) {
return a;
}
You try to add a method to the prototype of the instance of example (showExample). The instance has no prototype. Try example.prototype.display = function() {/*...*/}; (in other words, add the method to the prototype of the constructor of showExample, that is example) and check again. After that, all instances of example 'know' the display method, or in your words, display is 'public' to all instances.
You can add the method to the instance using showExample.display = function() {/*...*/};. Using that, only showExample knows the the display method.
in your case showExample is an object of example...
use
example.prototype.display = function(a)...

Create a JavaScript function dynamically from a string name

Given a string classname, I want to dynamically create a new JavaScript function named after that string that can be used to instantiate objects.
I've tried using eval() but for some reason the declared function does not appear in the global (window) scope.
eval( "function " + classname + "() {}" );
window[ classname ]; // => undefined
Is there a way I can dynamically create a new function named after a string?
Or, alternatively, give me some way to reference the created function after creating it via eval. Interestingly it appears as a local variable when I debug it in Safari.
Update:
Got it! Of course it's obvious, I just use eval again to create the instance:
var myInstance = eval( "new " + classname );
myInstance.constructor.name; // => classname (yay)
This should work in my case because I only need to create one instance of the class right after it's declared. For the general case though see Pointy's answer.
Yes:
window[classname] = function() { ... };
Now, in honesty, that's not exactly like what you were attempting, but it's pretty close. When you instantiate a function via a function expression like that, and without a name, the function can't refer to itself except via the name in the outer scope (in this case, the global scope).
If that's important, what you could do is this: create the function with some stock "internal" name, and then assign it to the global name:
function secretName() { ... }
window[classname] = secretName;
function registerFunction(functionBody) {
"use strict";
var script = document.createElement("script");
script.innerHTML = "function " + functionBody;
document.body.appendChild(script);
}
registerFunction("fooBar(x, y) { return x + y; }");
fooBar(1, 2); // will give you 3
Although this is essentially the same as eval() but it will register the function in the domain of the current page. You can later remove this script element, or reuse it for other functions.
Try this:
var classname = "myFunction";
window[ classname ] = function () {};
alert( window[ classname ] ); // => undefined
In case you don't want to create new functions based on some string, but based on another similar function: (this might not be a good example but hope you can get the idea)
function createListOfFunctions(functionNameList) {
resultFunctions = {};
// Loop all names to create a list of functions with those names
$.each(functionNameList, function(index, functionName) {
resultFunctions[functionName] = _createFunction(functionName);
});
return resultFunctions;
}
function _createFunction(name) {
return function(anotherNameToCompare) {
// customize this funciton whatever you like
return name == anotherNameToCompare;
};
}
// USAGE:
functionNameList = ['stack', 'overflow'];
result = createListOfFunctions(functionNameList); // result = { stack: function(name) {...}, overflow: function(name) {...} }
result.stack('stack'); // true
result.stack('not stack'); // false
result.overflow('overflow'); // true

Categories

Resources