I am trying to use Backbone.js and Closure Compiler in advanced mode. I wrote a convenient function that creates getters/setter for my Backbone.Models using Backbone.Model.defaults, it looks like this:
some.defaultProperties = function(ctor) {
if (!ctor.prototype.defaults)
return;
var defattr = function(name) {
return {
get: function() {
return this.get(name);
},
set: function(val) {
var diff = {};
diff[name] = val;
return this.set(diff);
}
};
};
var props = {};
for (var attr in ctor.prototype.defaults) {
if (ctor.prototype.defaults.hasOwnProperty(attr))
props[attr] = defattr(attr);
}
Object.defineProperties(ctor.prototype, props);
};
Equivalent in CoffeeScript here http://srackham.wordpress.com/2011/10/16/getters-and-setters-for-backbone-model-attributes/
Now back to Closure Compiler in advanced mode. It doesn't seem to like it because I'm accessing those using normal property access syntax, ie. model.color instead of model.get('color'), that's the point. But Closure is mangling those, and therefore I get undefined instead of my value. Any workaround for this? (Except rewriting everything to use get('attrname'))?
UPDATE And of course, how would this work with Backbone.Model.hasChanged and other methods that take a string literal for attribute name.
You are defining your properties using quoted syntax:
props[attr]
But your problem is occurring when you try to access them via dotted syntax:
model.color
In ADVANCED_OPTIMAZATIONS, a cardinal rule is that you must reference a property consistently. See https://developers.google.com/closure/compiler/docs/api-tutorial3#propnames
It sounds like the easiest answer may be for you to use quoted syntax for these properties:
model['color']
However, you'll lose all renaming and dead code elimination for such properties.
Any properties you create dynamically are "external" in advanced mode and you have three choices: (1) use quoted access (as Chad suggests) or (2) create externs or (3) use Simple mode and give up property rename and global dead code removal.
Related
I've seen two different ways of implementing getters/setters in a module pattern. One uses "defineProperty", while the other doesn't. What are the advantages/disadvantages of one versus the other?
var MyModule = (function() {
var _val;
var api = {
get value1() {
return _val
},
set value1(value) {
_val = value
}
};
Object.defineProperty(api, 'value2', {
get: function() {
return _val;
},
set: function(value) {
_val = value
}
});
return api;
}());
https://plnkr.co/edit/TbJSD4noZTew8II83eTH?p=preview
Using getter syntax you create a property which, prior to ES2015, you had to know the name of at the time that you were writing the code.
Object.defineProperty allows you to perform the same as the above but, even before ES2015, does not require you to know the name of the property in advanced. It also allows you to decide if the property is configurable, enumerable, or writable which is not possible using the get/set syntax.
To answer your exact question: neither is better. They're for different situations. The get/set syntax is simpler to read and write but isn't as powerful as Object.defineProperty.
As mentioned in documentation.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Normal property addition through assignment creates properties which show up during property enumeration (for...in loop or Object.keys method), whose values may be changed, and which may be deleted. This method allows these extra details to be changed from their defaults. By default, values added using Object.defineProperty() are immutable.
One "advantage" of Object.defineProperty vs get/set notation is that defineProperty can be used at any time, even on objects that have already been created (on which you cannot use the shorthand 'get' notation). From the mdn:
To append a getter to an existing object later at any time, use Object.defineProperty().
I need to add a member to an HTMLElement, in other words, I need to store data into an element. This is what I would like to achieve as if I am coding in ScriptSharp.
/** My method */
public DoIt(element: Element, obj: Object) {
Literal("{0}.extended = {1}", element, obj); // This is not standard Typescript!
}
In my example ScriptSharp (a project to convert C# code into Javascript) provides a Script.Literal object that allows developers to write plain Javascript when a C# abstraction is not possible.
So that the Javascript output is:
// Probably Typescript will render it a bit differently, but basically
// this is what we get in the end...
var _doit = function(element, obj) {
element.extended = obj;
};
How can I achieve this in Typescript? Or maybe I should handle this problem in a different way?
Any valid JavaScript is also valid TypeScript.
This means that you can write literal JS in any place in your code.
var _doit = function(element, obj) {
element.extended = obj;
};
This is valid JS and TS.
However, since you use TypeScript, you may also want to use static typing with your code.
If you just add types to your code, it will compile correctly, but you'll get a semantic error:
var _doit = function(element:HTMLElement, obj) {
element.extended = obj; // error: HTMLElement doesn't have property 'extended'
};
To prevent this error, you can notify the compiler that you intend to create a new property on HTMLElement:
interface HTMLElement {
extended?: any;
}
Now the compiler knows that you have an (optional) property extended on HTMLElement and will compile without errors. You will also get code autocompletion on this property (and JSDoc if provided).
This worked for me:
class Foo {
public DoIt(element: Element, obj: Object) {
var anyElement: any = element;
anyElement.extended = obj;
}
}
The problem (as you probably noticed) is that Element does not declare a property with the name extended, so TypeScript does its job and enforces the Element type. If you want to work around this, you can use the any type to do this.
Or maybe I should handle this problem in a different way?
If you want to completely break out of the compiler checking just have a standard javascript file. Note that if you want to use this from typescript you will have to tell typescript about it (using ambient declarations).
Personal Advice: Just use TypeScript.
I've been checking out the Google Closure Compiler recently. I downloaded the .jar file and gave it a test drive. So far, I must say that I've been very impressed. I can certainly see its usefulness beyond minimization. Props to the Google team!
I do have one small gripe though. It seems to me that you only get two options as far as optimization goes. It's either SIMPLE_OPTIMIZATIONS or ADVANCED_OPTIMIZATIONS. The former, although adequate, is very simple IMHO. For one thing, unless I'm missing something, it leaves all property names untouched. It also does not remove unreachable code. On the other hand, the latter option is simply too destructive.
Now, I'm fairly new to JavaScript, so it's very probable that I'm missing something. If I say something stupid, feel free to school me. That said, I can understand the issues with renaming in JavaScript. The Google team recommends using the bracket notation (object['property']) instead of the dot notation (object.property) to access the properties that you do not want changed and never mixing the two uses. They also suggest 'exporting' methods by using the following pattern:
MyClass = function(name) {
this.myName = name;
};
MyClass.prototype.myMethod = function() {
alert(this.myName);
};
window['MyClass'] = MyClass; // <-- Constructor
MyClass.prototype['myMethod'] = MyClass.prototype.myMethod;
However, there are legitimate cases that you want to mix the two notations. Let's say we are building a game. The game's code is completely isolated inside a closure. It does not 'export' anything to the global scope, nor does it need to. In fact, it really should not touch the window object. However, it does need to read some in-game properties from XML configuration files.
Sample JavaScript:
var TheGreatAdventure = (function(window) {
function Fighter() {
// Private to application
this.id = 42;
// Accessible to XML configuration system
this.name = 'Generic Jen';
this.hitPoints = 100;
this.onAttack = genericFighterAttack;
this.onSpeak = genericFighterSpeak;
...
}
Fighter.publishedProperties = ['name', 'hitPoints', 'onAttack', 'onSpeak']
function genericFighterAttack() {...}
function genericFighterSpeak() {...}
function cassieAttack() {...}
function cassieSpeak() {...}
...
EntityReader = {
...
function readFromXMLNode(attributes, entityClass, entityInstance) {
for (var i = 0; i < attributes.length; i++) {
var attribute = attributes[i];
if (attribute.nodeName in entityClass.publishedProperties)
entityInstance[attribute.nodeName] = bindContext[attribute.value];
}
}
...
}
}(window));
Sample XML configuration file:
<Fighter name='Custom Cassie' onAttack='cassieAttack' onSpeak='cassieSpeak'/>
Not only would the above system fail to assign the properties, the functions cassieAttack and cassieSpeak would have been eliminated during minimization as dead code!
Now, there's no way I'm accessing all of the 'published' properties using the bracket notation throughout the game's code. Even if there's no run-time penalty in doing so (there should not be any), there's still a lot of extra typing involved and it's (IMO) an eyesore. With such common properties, everything would show up as a string inside a text editor, defeating the purpose of syntax highlighting!
It seems to me that a simple #preserve (or something similar) directive over those properties would allow ADVANCED_OPTIMIZATIONS to be used with minimum cost in final program size. Am I missing something?
This answer was completely rewritten, turns out there's a way to do what user1127813 wants.
You need to provide a property mapping file that maps some names to themselves, using the --property_map_input_file flag. Suppose you have the following original code in test.js:
/** #constructor */
function Fighter() {
this.ID = 42;
this.fullName = 'Generic Jen';
this.hitPoints = 100;
}
Fighter.publishedProperties = ['fullName', 'hitPoints'];
var jen = new Fighter();
var bob = new Fighter();
bob.ID = 54;
bob.fullName = 'Bob the Destructor';
bob.hitPoints = 1337;
for(i = 0; i < Fighter.publishedProperties.length; i++) {
prop = Fighter.publishedProperties[i];
alert(prop + ' = ' + bob[prop]);
}
Compile it like so:
java -jar closure-compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js test.js --property_map_output_file testprop.txt --js_output_file test2.js
You will get a new file test2.js (with contents that don't work) and another file testprop.txt that contains:
ID:a
hitPoints:c
fullName:b
Change testprop.txt so it looks like this:
ID:ID
hitPoints:hitPoints
fullName:fullName
Then recompile with testprop.txt as input instead of output:
java -jar closure-compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js test.js --property_map_input_file testprop.txt --js_output_file test2.js
Observe the contents of test2.js:
var a=["fullName","hitPoints"],b=new function(){};b.ID=54;b.fullName="Bob the Destructor";b.hitPoints=1337;for(i=0;i<a.length;i++)prop=a[i],alert(prop+" = "+b[prop]);
The desired properties are now accessed with their original name using the dot notation and the program will correctly show popups with the published properties of bob.
The compiler has support for this, but it is awkward, and depends on a "primitive" from the Closure Library called "goog.reflect.object":
/** #nocollapse */
Fighter.publishedProperties = goog.object.transpose(goog.reflect.object(
Fighter, {fullName:1, hitPoints:2}));
This avoid the use of quoted properties. If this is the only thing that you use from the Closure Library everything but the "goog.object.transpose" function will be compiled out (goog.object.transpose isn't special so you are free to use an alternate implementation). This is type safe and can be used with the compiler's type based optimizations (see a discription of that here: http://code.google.com/p/closure-compiler/wiki/ExperimentalTypeBasedPropertyRenaming ).
The key is that goog.reflect.object must be used directly, taking the constructor and an object literal with the keys of the properties you need to preserve.
The other thing that you will want to be aware of this that in ADVANCED mode, if Fighter.publishedProperties is defined in global scope, it will be removed from the constructor due to namespace collapsing. #nocollapse prevents this. An alternative would be to use a helper method to add the property:
function addPublishedProperties(obj, value) {
obj.publishedProperties = goog.object.transpose(value);
}
Ok, I've covered a lot of ground here so be sure to let me know if you would like clarification.
Surprisingly, this Apple page has Element.prototype equal to undefined, so I cannot use this awesome snippet of code.
Are there any reason for doing this?
Apple is using the Coherent JS framework which has this block of code:
// Trick picked up from Prototype to get around IE8's fixed Element & Event
(function() {
var element = this.Element;
this.Element = {};
Object.extend(this.Element, element || {});
}).call(window);
window.Element is originally a function, but it's being replaced and extended with a regular object. Only functions have .prototype properties.
Workaround:
The prototype chain for any HTML element seems to be:
Specific element type (HTMLBodyElement, HTMLDivElement, etc...)
HTMLElement
Element
Node
Object
You should be able to attach your referenced code to the prototype to any of the bold objects in the chain and get the style for an html element. I would not recommend this in production code as modifying objects like that is generally considered harmful, but if you are just trying to export a style from another website, it should work well enough.
Object.prototype.exportStyles = (function () { //Works if you use it on an element, the code will protect you from yourself if you try to use it on regular objects.
HTMLElement.prototype.exportStyles = (function () { //Safer because it is farther down the inheritance line, affecting fewer objects.
//Avoiding name collisions and other surprises.
In addition to what Dennis explained well, the easiest solution to avoid changing built-in objects (which people seem to love to do over and over, as Apple did on their site and Luc1245 did in the post you've mentioned).
A non-intrusive alternative is to run something like:
function exportStyles = ( function ( /* what Luc1245 posted */;
exportStyles.apply( /* this */ theElement, /* args */ []);
It seems that they have overwritten the default value of Element and assigned it the value of an object instance, which by default doesn't have the prototype property. Try the following in the console:
var a = {};
console.log(typeof a.prototype === 'undefined');
function abc() {}
Element = abc;
var b = new Element();
console.log(typeof b.prototype === 'undefined');
There isn't an universal reason to override built-in functions, so I'd guess it's probably because they thought it would make the most sense semantically (as it seems the Element object is used for DOM manipulation) and they don't have the possibility of conflicting with external libraries, which is why it's usually discouraged.
I've recently tested UglifyJS and YUI Compressor and noticed something odd.
Both minifiers don't seem to change the names of object properties, only the names of variables and functions.
for instance if I have the following code:
var objName = {first:2, second:4};
alert(objName.first + " " + objName.second);
the names first and second remain unchanged in the minified version.
Why is that?
Since in javascript a new scope is created in a function, you can scope your code in an immediately invoked function.
// scoped
(function() {
var objName = {first:2, second:4};
alert(objName.first + " " + objName.second);
})();
Then using Google's Closure Compiler, if you turn on the "Advanced" optimization it will see that the properties are only used locally, and will obfuscate them.
// result
var a={a:2,b:4};alert(a.a+" "+a.b);
It's because it doesn't know where the object is going to be used. It could be used externally by other code and you wouldn't want your other code to have to change whenever you obfuscate it.
Edit So basically, it's like that to prevent obfuscation from breaking external/internal references to properties that may not be possible to figure out while obfuscating.
Since there are no well defined scoping rules around objects in JavaScript it's impossible to obfuscate the names in a way that is guaranteed to be correct.
For example, if you had the following function:
function f() {
return { first: 'foo', second: 'bar' };
}
In order to obfuscate the property names you would have to nail down all the places that f is called from. Since functions are first-class in JavaScript they can be assigned and passed around in arbitrary ways making it impossible to pin down where f is referenced without actually running the program.
Additionally, JavaScript doesn't have any way for you to specify intent around what's public API and what isn't. Even if the minimizer could reliably determine where the function is called from in the code you give it, there would be no way for it to make the same changes to code that it hasn't seen.
I guess that's because the minifiers would break the object properties. Consider this:
function getProp(ob,name) {
return ob[name];
}
var objName = {first: 2, second: 4};
var prop = getProp(objName, "second");
There's no way for the minifier to know the string literal "second" being an object property. The minified code could look like this then:
function a(b,c){return b[c]}var d={p1:2,p2:4};var e=a(d,"second")
Broken now.
The latest release of uglify (today) has object property mangling, see v2.4.19. It also supports reserved files for excluding both object properties and variables that you don't want mangled. Check it out.
The only public tool so far to obfuscate property and function names (afaik) is the Closure Compiler's Advanced mode. There are a lot of limitations and restrictions, but the end result is generally worth it.
As a passing note: the Dojo Toolkit is compatible (with some minor modifications) with the Closure Compiler in Advanced mode -- arguably the only large-scale public JavaScript library that can be fully obfuscated. So if you are looking at obfuscation to protect your IP, you should look into using Dojo for the task.
http://dojo-toolkit.33424.n3.nabble.com/file/n2636749/Using_the_Dojo_Toolkit_with_the_Closure_Compiler.pdf?by-user=t
Stephen
What about doing something like:
// scoped
(function() {
var objName = {first:2, second:4};
var vA = 'first';
var vB = 'second';
alert(objName[vA] + " " + objName[vB]);
})();
Once objName.first and/or objName.second are referenced enough times, this technique will start to save characters. I can't think of any reason that wouldn't work, but I can't find any minifiers that do it.