I'm trying to use an IIFE as a method (which might be wrong).
Why ? Because, I'm trying to implement the proxy design pattern.
In adobe extendscript, there is an "app" object to access documents, etc, like -
var length = app.activeDocument.length; // or some other property
Now, I wanted to put a proxy around "app". So I created a proxy object -
var AppProxy = {
activeDocument: function() { // do stuff...; return app.ActiveDocument; }
}
But now, this is how I had to access it -
var length = AppProxy.activeDocument().length;
But this is how I want to access it -
var length = AppProxy.activeDocument.length; // no parenthesis
So I read about IIFE, and ended up doing this -
var AppProxy = {
activeDocument: (function() {
// do stuff...;
return app.ActiveDocument; })()
}
And as expected, the AppProxy.activeDocument gets called automatically when AppProxy is defined, i.e. even BEFORE it reaches var length = AppProxy.activeDocument.length.
So how do I prevent this from happening when AppProxy is defined as an object literal ?
Is there a workaround to my requirement ?
Thanks.
But this is how I want to access it -
var length = AppProxy.activeDocument.length; // no parenthesis
To do that, you need to define activeDocument as a property with a getter function. This is possible in browsers with proper support for ES5's getters and setters, which is all modern browsers (not IE8 and earlier). (Before ES5, there was a never-standardized syntax for this that some browsers supported, but again not IE8 or earlier).
In ES5, you can do this either with Object.defineProperty or by defining a getter in an object initializer. Here's Object.defineProperty:
// ES5+ only
var AppProxy = {};
Object.defineProperty(AppProxy, "activeDocument", {
get: function() {
// do stuff...;
return app.ActiveDocument;
}
});
Here's doing it as part of the object initializer:
// ES5+ only
var AppProxy = {
get activeDocument() {
// do stuff...;
return app.ActiveDocument;
}
};
Having done either of those, then this:
var length = AppProxy.activeDocument.length;
...runs that function, even though it doesn't look like it does. The function call still happens, it's just hidden.
But if you need to support obsolete browsers (even here in mid 2016, IE8 still has ~5% market share) or if you don't want to hide the fact you're calling a function, just call the function:
var length = AppProxy.activeDocument().length;
Related
I'm working on an AngularJS SPA and I'm using prototypes in order to add behavior to objects that are incoming through AJAX as JSON. Let's say I just got a timetable x from an AJAX call.
I've defined Timetable.prototype.SomeMethod = function() and I use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf in order to set the prototype of x to TimeTable.prototype. I have the polyfill in place too.
If I call x.SomeMethod() this works in IE > 9, FF, Chrome etc. However, IE 9 gives me a headache and says throws an error stating 'x does not have property or member SomeMethod'.
Debugging in IE shows me that the _proto_ of x has SomeMethod() in the list of functions, however, calling x.SomeMethod() gives the same error as described.
How can I make this work in IE9 ?
More comment than answer
The main problem with "extending" a random object retrieved from some other environment is that javascript doesn't really allow random property names, e.g. the random object may have a property name that shadows an inherited property. You might consider the following.
Use the random object purely as data and pass it to methods that access the data and do what you want, e.g.
function getName(obj) {
return obj.name;
}
So when calling methods you pass the object to a function that acts on the object and you are free to add and modify properties directly on the object.
Another is to create an instance with the methods you want and copy the object's properties to it, but then you still have the issue of not allowing random property names. But that can be mitigated by using names for inherited properties that are unlikely to clash, e.g. prefixed with _ or __ (which is a bit ugly), or use a naming convention like getSomething, setSomething, calcLength and so on.
So if obj represents data for a person, you might do:
// Setup
function Person(obj){
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
this[p] = obj[p];
}
}
}
Person.prototype.getName = function(){
return this.name;
};
// Object generated from JSON
var dataFred = {name:'fred'};
// Create a new Person based on data
var p = new Person(dataFred);
You might even use the data object to create instances from various consructors, e.g. a data object might represent multiple people, or a person and their address, which might create two related objects.
This is how I solved it at the end:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
if (!isIE9()) {
obj.__proto__ = proto;
} else {
/** IE9 fix - copy object methods from the protype to the new object **/
for (var prop in proto) {
obj[prop] = proto[prop];
}
}
return obj;
};
var isIE9 = function() {
return navigator.appVersion.indexOf("MSIE 9") > 0;
};
Probably not the correct word. But I want to create a new type in JavaScript. It would have the simple property that one could do this:
var inst = new SomeType();
inst.key1.key2 = 'something';
inst.key1.key1.key3 = 'something';
Basically you wouldn't have to declare an object literal to extend further. It would create one automatically.
This would allow me to build complex structures without having to worry about checking for the existence of a property to extend off of.
Instead of doing
inst.key1 = {};
inst.key1.key2 = 'data';
one could just do
inst.key1.key2 = 'data';
and the
inst.key1 = {};
would be automatic, i.e. would happen internally.
This does have a practical purpose. Particularly I have a registry pattern which I would use this new type to organize data using a more hierarchical approach.
Also, I see a pattern, common in libraries, that tests for the existence of an object literal and then creates one if it does not exist.
This is a common idiom it seems.
What you'd like to get can easily be achieved with Harmony proxies (MDN). However, this API is not yet stable, so it's fine to use it in non-critical hobby code, but don't use it in production code (yet).
Here's an extremely simple example:
function getFreeChain(object) {
var handler = {
get: function(target, name) {
if (name in target)
return target[name];
var newTarget = target[name] = {};
return new Proxy(newTarget, handler);
}
};
return new Proxy(object, handler);
}
// Usage:
var obj = {};
var magicalObj = getFreeChain(obj);
magicalObj.a.b.c.d.e.g = 1;
console.log(magicalObj.a.b.c.d.e.g); // 1
console.log(obj.a.b.c.d.e.g); // 1
obj.x.y.z = 1; // TypeError: obj.x is undefined
Note: My example is an implementation of the latest Proxy specification, which is only supported by Firefox .
Chrome only supports an old, significantly different version of the API, which can be enabled by turning on "Experimental JavaScript" at chrome://flags/. This old API is ugly, and implementing the previous would require significantly more lines of code, so I'll leave this as an exercise to the reader.
Oh, there's a library called DirectProxies.js (superseded by harmony-reflect) which brings the simple Proxy API to Chrome. After including the this library, the previous code will work in Firefox and Chrome (with experiments enabled): http://jsfiddle.net/PAhYL/
Because javascript does not support operator overloading, one could create a really ugly fake operator called ._ as such. If someone wants to implement ._ please edit answer.
http://jsfiddle.net/586Sc/
var Obj = function () {
this.hold = {};
};
Obj.prototype._ = function (key) {
//implement here;
return this;
};
var obj = new Obj();
obj._('key1')._('key2')._('key3') = 'barbarella';
console.log(test);
var obj = {
destroy: function(){this = null;}
};
obj.destroy();
This works in Chrome, however firefox is throwing an error referencing this for some reason. Is there a better way to kill this object within a method?
Error:
invalid assignment left-hand side
[Break On This Error] destroy: function(){this = null;}
Not sure why Chrome allows for it but you can't assign a value to this. You can reference this, but you can't assign a value to it.
If you have some array destruction you want to perform you can reference this.myArrayName within your destroy method and free up whatever you're trying to release, but you can't just assign null to this to destroy an instance.
I suppose you could try something like this:
var foo = {
// will nullify all properties/methods of foo on dispose
dispose: function () { for (var key in this) this[key] = null; }
}
foo.dispose();
Pretty much as close as you can get to legally nullifying "this"...
Happy coding.
B
Call me old fashion, but:
foo = null;
I'm not sure why you're making this difficult. Javascript is a garbage collected language. All you have to do to allow something to be freed is to make sure there are no more references to it anywhere.
So, if you start with:
var obj = {
data: "foo";
};
and now you want to get rid or "free" that object, all you have to do is clear the reference to it with:
obj = null;
Since there are no longer any references in your code to that data structure that you originally defined and assigned to obj, the garbage collector will free it.
An object cannot destroy itself (because other things may have references to it). You allow it to be freed by removing all references to it. An object can clear out it's own references to other things, though that is generally not required as removing all references to the object itself will also take care of the references it holds (with the exception of some bugs with circular references between JS and the DOM in certain older browsers - particular IE).
One time when you might explicitly "delete" something is if you have a property on an object that you wish to remove. So, if you have:
var obj = {
data: "foo",
count: 4
};
And you wish to remove the "data" property, you can do that with this:
delete obj.data;
of if the property/key was assigned programmatically via a variable like this:
var key = "xxx";
obj[key] = "foo";
you can remove that key with:
delete obj[key];
https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Object/watch
The .watch() method does this in short: "Watches for a property to be assigned a value and runs a function when that occurs."
Long descriptive form: "Watches for assignment to a property named prop in this object, calling handler(prop, oldval, newval) whenever prop is set and storing the return value in that property. A watchpoint can filter (or nullify) the value assignment, by returning a modified newval (or by returning oldval)."
There is a question for getting it to work in all browsers here: Object.watch() for all browsers?
I am looking for something similar to that. What I'm looking for is a method I can use to fit this specification: "Watches for assignment to any property in this object and runs a function when that occurs." The main difference is that it is any property, and just any specific property.
Can somebody create such a method or if they know such a method is already in existence, link to it? It'd be useful to have it work in all browsers (minus IE, or minus IE8 if IE9 conforms)
Edit: For an example of what I mean, I'll show what I need it for.
var DiscreteLine = function (leftBound, length){
this.positive = [];
this.negative = [];
this.length = length;
this.leftBound = leftBound;
this.rightBound = leftBound + length
if (this.leftBound < 0){
this.negative.length = (leftBound * -1) + 1;
} else {
this.negative.length = 0;
}
if (this.rightBound >= 0){
this.positive.length = rightBound + 1;
} else {
this.positive.length = 0;
}
this.watchObject = new ObjectWatcher(handler(prop, oldval, newval){ /* some stuff */ });
}
Then, when for example, if somebody did the following:
theLine = new DiscreteLine(-2, 4);
theLine[-8] = 10;
The handler would call, with the arguments ("-8", undefined, 10). (What would end up happening is, is that the script would recalculate leftBound and length properties automatically (like how Arrays automatically update the length property).
This would basically require overriding the setter for a property of an already defined object. As far as I know, this is only possible in ECMAScript 5 (via Object.defineProperty) and at the moment, I'm not sure which browsers support this, if at all, so it wouldn't be "cross-browser" as you had asked.
Edit: Your example makes your requirement clear now. I'm afraid the language doesn't provide any way to be notified when new properties are added to an object. In your example, you really don't have any choice except to replace the array notation with a function call.
Does anyone know if there is a way to get JavaScript function name. For example I got a function like
function test1(){
alert(1);
}
I have it in my head section. Then I create an object obj1 and put my function there
obj1.func = test1;
When I call a method in obj1 object, do I have any way to get my function name (test1) inside of this method, except parsing the source (this.func.toString()) of the function.
function test() { alert(arguments.callee.name); }
b = test;
b();
outputs "test" (in Chrome, Firefox and probably Safari). However, arguments.callee.name is only available from inside the function.
If you want to get name from outside you may parse it out of:
b.toString();
but I think name property of function object might be what you need:
alert(b.name);
this however does not seem work for IE and Opera so you are left with parsing it out manually in those browsers.
Until ES2015, there was no standard way to get the name of a function. Most current browsers support a name property on Function objects that was non-standard until ES2015, but no current version of IE does. The only option this leaves you if you need to support IE is trying to parse the name from the function's string representation, which is not a good idea. There's a (long) discussion here about it: http://groups.google.com/group/comp.lang.javascript/browse_frm/thread/b85dfb2f2006c9f0
The best thing to do is:
function functionName(fun) {
var ret = fun.toString();
ret = ret.substr('function '.length);
ret = ret.substr(0, ret.indexOf('('));
return ret;
}
Note: Using Function.caller is non-standard and arguments.callee is forbidden in strict mode.
Here's what I use to put class names in error messages. It includes code to get the name of functions, which works in most browsers.
Obviously, there is no standard way that always works, so you should always provide a name that can be used if no other name is found.
var nameFromToStringRegex = /^function\s?([^\s(]*)/;
/**
* Gets the classname of an object or function if it can. Otherwise returns the provided default.
*
* Getting the name of a function is not a standard feature, so while this will work in many
* cases, it should not be relied upon except for informational messages (e.g. logging and Error
* messages).
*
* #private
*/
function className(object, defaultName) {
var result = "";
if (typeof object === 'function') {
result = object.name || object.toString().match(nameFromToStringRegex)[1];
} else if (typeof object.constructor === 'function') {
result = className(object.constructor, defaultName);
}
return result || defaultName;
}
This is probably the best way to do it:
var myfunc = function () {};
var funcName = myfunc.constructor.name;
This can be done outside the execution of the function, and you can check within the context of the browser console.
Happy coding!
One interesting way I'm experimenting with is a declaration like the following:
var test1 = function test1(){
alert(1);
};
It's a little hacky, but what ends up happening is test1 is a local variable that holds a [Function: test1] object.
Here's what happens when you use code based on it:
test1(); //runs the function as expected
console.log(test1.name); //prints 'test1'
So if you do the following:
obj1.func = test1;
You'll then be able to reference obj1.func.name and it'll return 'test1'.
You could convert your function into a string (fn + '') and split it later at whitespace and open bracket /\s|\(/.
var getFunctionName = function (fn) {
return (fn + '').split(/\s|\(/)[1];
};