To get around what has proven to be a scope limitation to me (as answered here), I've written a piece of code which inserts a line in an anonymous function so that whoever writes the function doesn't have to do it themselves. It's a bit hacky (actually, it feels quite a lot hacky), and I really don't know what I'm doing, so I'd appreciate an expert eye to catch any errors I may have missed or point out any dangers I'm unaware of. Here's the code:
function myObj(testFunc) {
this.testFunc = testFunc;
this.Foo = function Foo(test) {
this.test = test;
this.saySomething = function(text) {
alert(text);
};
};
var Foo = this.Foo;
var funcSep = this.testFunc.toString().split("{");
funcSep.splice(0, 1);
funcSep = funcSep.join("{");
var compFunc = " var Foo = this.Foo;" + funcSep;
compFunc = compFunc.split("}");
compFunc.splice(compFunc.length - 1, 1);
compFunc.join("}");
var otherTestFunc = new Function(compFunc);
otherTestFunc.apply(this);
}
var test = new myObj(function() {
var test = new Foo();
test.saySomething("Hello world");
});
The function above evaluates as expected, and I don't need to force whoever writes the anonymous function to obtain access to Foo by using this.Foo. This approach feels iffy, though. Is what I'm doing acceptable, and if not, are there any ways to circumvent it?
Also, the only reason I didn't include this in my original question is that seems like something of a departure from the original context of the question.
You're trying to break the language. Don't do that. It's not Java.
Developers have certain expectations on the behaviour and scope of variables, and your approach would rather confuse them. Think about the following:
var Foo = SomeWonderfulClass;
var test = new myObj(function() {
var test = new Foo();
// ...
});
Now the developer wants to instantiate SomeWonderfulClass, but your magic messes around with that.
On the other hand, this would work fine, even with your trickery:
var test = new myObj(function() {
var Foo = SomeWonderfulClass;
var test = new Foo();
// ...
});
But the bigger problem is that the actual scope is lost:
var Bananas = SomeWonderfulClass;
var test = new myObj(function() {
var test = new Bananas(); // Error: Bananas is undefined!
});
Nobody expects such shenanigans.
That being said, there's some things about your code to be improved:
this.Foo is initialized with every new object. That's not necessary. Better use
myObj.prototype.Foo = function () {...}
The line var Foo = this.Foo; is not needed in myObj.
Your string magic is overly complex. How about
var otherTestFunc = new Function(testFunc.toString()
.replace(/^[^{]+{/, '{var Foo=this.Foo;'));
No need to remove the braces.
(testFunc does not accept any arguments, but I guess you know that.)
So that boils down to
function myObj(testFunc) {
this.testFunc = testFunc;
var otherTestFunc = new Function(testFunc.toString()
.replace(/^[^{]+{/, '{var Foo=this.Foo;'));
otherTestFunc.apply(this);
}
myObj.prototype.Foo = function Foo(test) {
this.test = test;
this.saySomething = function(text) {
alert(text);
};
};
I've been bothered by this method since I saw it in asp.net validation code (!). Not really safe for arbitrary function:
var f = (function () {
var closure = 1
return function (argument) {
alert(argument)
alert(closure)
}
})()
var f2 = new Function(f.toString().replace(/^function.*?{([\s\S]*)}$/, 'alert(1);$1'))
f2(1) // :(
Arguments could be saved though.
Related
Trying to implement a Javascript sandboxing scheme and now I've run into a bit of a wrinkle. I need to pass a "context" parameter to the code which will essentially serve as a handle to the "global" object, but so far no luck.
To illustrate the problem with a simple example, consider this bit of code:
var foo = new Function(" this.baz = this.mux; return this ");
foo.mux = "mux";
foo.call();
console.log(foo.baz);
console.log(foo.toString());
Output:
$> undefined
$> function anonymous() { this.mux; return this; }
It obviously doesn't work because the Function object doesn't seem to get it's own this like ordinary functions created with new.
So...is there any way to "reseat" a Function's this to point to itself beforehand (or just any other way around the issue)?
EDIT
Okay, so from what I understand from the comments section I'm going to need a constructed object.
var foo = new Function(" return new function(){ this.baz /* = ?? */; return this; } ");
Is there a way to somehow access the enclosing anonymous function's properties? Like "this.mux = foo.mux" (except of course "foo" isn't visible from that scope)?
I think your getting confused on what new Function( does,. It does not create an instance of an object, it just create a function. So like any object instances you will also need to use new on these.
So you need 2 steps..
create the function that you will be creating an object from..
with this function create an instance using new..
Below is a simple example..
var fcreate =
new Function('global', "this.global = global");
var f = new fcreate("hello");
console.log(f.global);
If your not bothered about instances, we can forget about this altogether, and just create a captured scope as a parameter..
eg..
var f = new Function("global", "console.log(global)");
f("This is a global to function");
f("This is another one");
You can pass foo as a parameter of call:
var foo = new Function(" this.baz = this.mux; return this ");
foo.mux = "mux";
foo.call(foo); // <-- this
Edit: Although the code above works, I wouldn't recommend it. You will be better off creating the function/class foo:
var Foo = function(mux){
this.baz = mux;
}
var foo = new Foo("mux");
console.log(foo.baz);
The best I could come up with that actually works.
var foo = new Function(" this.baz = this.mux; return this ");
var context = { mux: "mux" };
foo = foo.bind(context);
foo();
// context.baz == "mux"
Alright so this is in fact doable, and it's basically an extension of Keith's answer:
function verify(condition)
{
console.log(condition === true ? "pass" : "fail");
}
function test()
{
if(!(this instanceof test))
return new test();
var foo = new Function("mux", "return new function(){ this.baz = mux; return this; } ");
var bar = new foo(null);
verify(bar.baz === null);
var zim = new foo(this);
verify(zim.baz === this);
var qud = new foo(global);
verify(qud.baz === global);
};
test();
Output:
pass
pass
pass
A sincere thanks to everyone for helping me figure this one out - cheers!
* EDIT *
As per Keith's comments, the correct implementation would simply be:
function verify(condition)
{
console.log(condition === true ? "pass" : "fail");
}
function test()
{
if(!(this instanceof test))
return new test();
var foo = new Function("mux", "this.baz = mux; return this; ");
var bar = new foo(null);
verify(bar.baz === null);
var zim = new foo(this);
verify(zim.baz === this);
var qud = new foo(global);
verify(qud.baz === global);
};
test();
In a project i experience problems with a javascript-scope. This is a basic question, but since im relatively new to js, it is hard to see the problem with this code.
The exception i got is: Uncaught TypeError: Cannot read property 'firstProperty' of undefined.
The jsfiddle
The code from the fiddle:
var someClass = function(){
var _someClass = {
firstProperty: 'hello world',
secondProperty: _someClass.firstProperty, // This line is not working like I expected it to work
}
return _someClass;
}
var someObject = new someClass();
If you want to reference firstProperty, then you can use something like this:
var someClass = function() {
var _someClass = new (function() {
this.firstProperty = 'hello world';
this.secondProperty = this.firstProperty;
})();
return _someClass;
}
var someObject = new someClass();
console.log(someObject.firstProperty);
console.log(someObject.secondProperty);
See it on JSFiddle.
This is because the _someClass.firstProperty is not yet defined. To make this work you should do something like this:
var someClass = function(){
var _someClass = {};
_someClass.firstProperty = 'hello world';
_someClass.secondProperty = _someClass.firstProperty;
return _someClass;
}
// The new here isn't actually necesary,
// since the object is created at the first
// line of the function. I actually don't
// know what happens here
var someObject = new someClass();
Also, to avoid future headaches, keep in mind that JS does not have classes. someClass is:
An object
A function
And since functions are objects and can have properties, you can use it as an object constructor but now I'm going off topic so I'll stop
This will help you look up more relevant information in the future
I need a mechanism whereby people can extend my base code with their own modules - but I'm struggling to come-up with a simple mechanism to do that.
Example: a function called 'test' which users can extend. Each user module is loaded after the original - so each one needs to build on the last (the order they're loaded should not matter or can be controlled by naming)
I started to play with something like this
var test = function() { // the master function
console.log("1");
}
var ltest = test; // module 1
var test = function() {
ltest();
console.log("2");
}
var ltest2 = test; // module 2
var test = function() {
ltest2();
console.log("3");
}
Then, when 'test' is called, it will run everyone's code (assuming no-one forgot their callback!!)
That works, but it relies on each module declaring it's own, unique 'callback' variable (ltest, ltest2) - if someone uses the same variable, we'll get a 'call stack exceeded' as those variables are global in scope...
Can anyone suggest a cleverer/better system - or point me to some examples of the same thing?
There's loads of material on inheritance but I don't want to create new things which extend the old one - I just want to extend the old one!!
p.s. taking the anonymous function stuff from the module pattern - I got this
var test = function() {
console.log("1");
}
(function() {
var oldtest = test;
test = function() {
oldtest();
console.log("2");
}
}())
(function() {
var oldtest = test;
test = function() {
oldtest();
console.log("3");
}
}())
Which is probably the simplest solution to my question - but not necessarily the best system to use (as it's dependant on the author to remember to callback the code - a dodgy module would break everything)
The Module Pattern is what you need.
In particular the 'Augmentation' or 'Loose Augmentation' patterns :
var MODULE = (function (my) {
var old_moduleMethod = my.moduleMethod;
my.moduleMethod = function () {
// method override, has access to old through old_moduleMethod...
};
return my;
}(MODULE || {}));
You could make a function like this
function extendFunction(fn, pre, post) {
return function () {
var arg = arguments;
if (pre) arg = pre.apply(this, arg); // call pre with arguments
arg = fn.apply(this, arg); // call fn with return of pre
if (post) arg = post.apply(this, arg); // call post with return of fn
return arg;
};
}
then extend as follows
var test = function () { // the master function
console.log("1");
};
test = extendFunction(
test, // function to extend
null, // thing to do first
function() {console.log("2");} // thing to do after
);
test = extendFunction(
test,
null,
function() {console.log("3");}
);
test(); // 1, 2, 3
This is very different to the normal meaning of "extend" though, where you give new properties to Objects or set up prototype chains, and to "module" which normally involves wrapping all your code in a function expression so that you don't pollute the namespace.
So there has been much discussion on the topic of accessing private members inside of prototype methods. The thought occurred to me that the following should work:
function Test(){
var private = "Private";
this.instance = function(){
return private;
};
Test.prototype.getPrivate = function(){
return private;
};
}
var test1 = new Test();
var test2 = new Test();
console.log(test1.instance === test2.instance); // false
console.log(test1.getPrivate === test2.getPrivate); // true
Turns out it does, in fact, work. I'm concerned, however, that there might be a drawback to doing this.
So my question is: Is there a drawback?
This doesn't work the way you probably expect, as test1's getPrivate() gets test2's private.
function Test(value){
var private = value;
this.instance = function(){ return private; };
Test.prototype.getPrivate = function(){
return private;
};
}
var test1 = new Test("test1");
var test2 = new Test("test2");
console.log(test1.getPrivate()); // test2
console.log(test2.getPrivate()); // test2
so it really doesn't matter if it is inefficient as it doesn't work.
I believe you did make a mistake in defining the prototype function inside of the function itself. This way everytime an instance is generated the prototype method available to all instances is overwritten ... that's the strange thing you're seeing I guess.
function Test(param){
var private = param;
this._getPrivate = function(){
return private;
};
}
Test.prototype.getPrivate = function(){
return this.instance();
};
var test1 = new Test("One");
var test2 = new Test(2);
console.log(test1.getPrivate());
console.log(test2.getPrivate());
This one works as expected.
But then, I don't understand what you need the prototype function for ... if you just defined the closure as a member-function, like you do (adding it to this instead of making it local), you get the same syntax as with using prototype. Hmmm, don't quite get what you intended - could it be you were just playing around with prototype?? gg
But then, if you're interested in accessing properties have a look at this code (EcmaScript 5 defineProperty) I took out of the - methinks - amazing prototypal tool (that comes without Prototypes drawbacks) Sugar ... (they actually use it to enable Events on PropertyChange! How very cool, anyway, doesn't work in legacy browsers <-> ES 5!)
Object.defineProperty(myObj, MyProp, {
'enumerable' : true,
'configurable': true,
'get': function() {
return value;
},
'set': function(to) {
value = calculateSomething(to);
}
});
The code below is adapted from this answer
function MessageClass() {
var self = this;
this.clickHander = function(e) { self.someoneClickedMe = true; };
var _private = 0;
this.getPrivate = function() { return _private; };
this.setPrivate = function(val) { _private = val; };
}
ErrorMessageClass.prototype = new MessageClass();
function ErrorMessageClass() {
MessageClass.apply(this, arguments);
}
var errorA = new ErrorMessageClass();
var errorB = new ErrorMessageClass();
errorA.setPrivate('A');
errorB.setPrivate('B');
console.log(errorA.getPrivate());
console.log(errorB.getPrivate());
The original post did not have the MessageClass.apply(this, arguments); since the purpose was to show how inheritance can go wrong in Javascript.
My question is, is saying: ErrorMessageClass.prototype = new MessageClass(); before the ErrorMessageClass constructor has even been declared bad practice? My understanding is that calling undeclared identifiers like that causes a silent declaration to occur, with the result being placed on the global window object, which I understand is bad.
Is this form:
function ErrorMessageClass() {
MessageClass.apply(this, arguments);
}
ErrorMessageClass.prototype = new MessageClass();
considered to be better practice? This link shows the code written as it was originally above, which is why I even tried it. Does this blogger know something I don't (quite likely)?
EDIT
Lots of great info in the answers below, but I did want to highlight this link which really explains things perfectly
Usually, to avoid this confusion, you would just attach the prototype after, but as Adam Rackis pointed out, function declarations are hoisted, like var statements.
However, you should not instantiate the base object as the prototype. If your base object takes arguments, what are you supposed to use? Use an empty "surrogate" constructor
// Used to setup inheritance
function surrogate () {};
function MessageClass() {
var self = this;
this.clickHander = function(e) { self.someoneClickedMe = true; };
var _private = 0;
this.getPrivate = function() { return _private; };
this.setPrivate = function(val) { _private = val; };
}
// The key steps to creating clean inheritance
surrogate.prototype = MessageClass;
// Sets up inheritance without instantiating a base class object
ErrorMessageClass.prototype = new surrogate();
// Fix the constructor property
ErrorMessageClass.prototype.constructor = ErrorMessageClass
function ErrorMessageClass() {
MessageClass.apply(this, arguments);
}
There's much more to be said. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
It works because function declarations are evaluated first. If you tried to move these classes under an object literal "namespace" the first version would fail.
I personally find the second method to be much easier to read - also, don't forget to set the sub-class' prototype.constructor property back to itself. Personally, I use an inherits() method on the Function prototype which wraps up essentially the type of code you're using here.