I have a bunch of JavaScript prototypes inheriting from each other. Each class is defined in its own file like this. An example file looks like this:
goog.provide("app.classes.ClassA");
(function() {
app.classes.ClassA = function() {
// constructor
}
app.classes.ClassA.prototype.example = function() {
// example method
}
})();
A second file may look like this:
goog.provide("app.classes.ClassB");
goog.require("app.classes.ClassA");
(function() {
app.classes.ClassB = function() {
// constructor
}
goog.inherits(app.classes.ClassB, app.classes.ClassA);
app.classes.ClassB.prototype.example = function() {
// example method
}
})();
After running the compiler, the code specified in the first file looks like this:
goog.provide("app.classes.ClassA");
(function() {
a.classes.ClassA = function() {
// constructor
}
a.classes.ClassA.prototype.example = function() {
// example method
}
})();
'app' in ClassA has been replaced by a single 'a' and goog.inherits cannot find app.classes.ClassA because it is undefined.
How can I prevent the Closure Compiler from renaming namespaces like this?
Thanks!
Two ways, first you can annotate your external methods/variables/classes with #expose:
(function() {
/**
* #expose
*/
a.classes.ClassA = function() {
// constructor
}
a.classes.ClassA.prototype.example = function() {
// example method
}
})();
Or, you can disable optimizations when running the closure compiler:
java -jar ccompiler.jar --compilation_level WHITESPACE_ONLY ..
Related
I'm writing a JavaScript closure that contains classes and looks a bit like this:
// myClosure.js
var myClosure = (function() {
class myClass {
constructor(val) {
this.myValue = val;
}
myFunc() {
console.log("myClass says: " + this.myValue);
}
}
this.myClass = myClass;
});
I'd like to 'import' this closure into another JavaScript file and instantiate a myClass. I tried this:
// myApp.js
$.getScript('myModule.js', function(myClosure) {
var myClassInstance = new myClosure.myClass(7);
}
but I get an error claiming that myClosure.myClass is not a constructor.
Can someone please point to a simple example, or a better way of doing this altogether?
Couple of things, myClosure returns nothing, so you will never be able to access it. In your example you could just return this.
Next, you have not executed the closure, you can do this by putting () at then end to make it into an IFFE..
Below is an example.
// myClosure.js
var myClosure = (function() {
class myClass {
constructor(val) {
this.myValue = val;
}
myFunc() {
console.log("myClass says: " + this.myValue);
}
}
this.myClass = myClass;
return this; //add this
}()); //and <- () that.
var myClassInstance = new myClosure.myClass(7);
myClassInstance.myFunc();
this doesn't work anything like that
myClosure is a function which doesn't create a closure
The first argument to the callback to getScript is a string containing the script. It isn't a value from the script.
To get values from it, you need to access a global.
Typically, if you were using jQuery.getScript, it would look something like this:
// myModule.js
class myClass {
// etc
}
// main.js
$.getScript('myModule.js', function () {
const myInstance = new myClass(7);
});
Modern code would tend towards using ES6 modules:
// myModule.js
class myClass {
// etc
}
export default myClass;
// main.js
import myClass from "./myModule.js";
const myInstance = new myClass(7);
// HTML
<script type="module" src="main.js"></script>
… although that sacrifices Internet Explorer support, in which cases you would want to look at using a bundler tool like Webpack.
you need to export this function first to include it in another javascript file.
if you are using node simply use module.exports = function name or for es6 you can use export default
and in another file simply import it
Let's say I have a class A defined in its own JavaScript file, like this:
A.js
class A {
constructor() {
// blah blah blah
}
func() {
// a long function
}
}
If I have a function (e.g. func()) that I want to be contained in its own file (for organizational purposes), how would I accomplish this?
What I want is something like this:
A.js
class A {
constructor() {
this.func = {};
}
} exports.A = A;
ADefinition.js
var A = require('./A.js');
A.func = () => {
// a long function
}
This obviously doesn't work, but how would one accomplish this?
Classes are mostly just syntax sugar. In ES5, you define prototype functions by assigning to the prototype:
function A() {
}
A.prototype.func = function() {
}
Classes can work the same way:
var A = require('./A.js');
A.prototype.func = () => {
// a long function
}
Though, note that if you use an arrow function, you won't have access to the instance - you may well need a full-fledged function instead:
A.prototype.func = function() {
// a long function
};
Also, personally, I think I'd prefer to put func on the class next to the class definition for code clarity, rather than running a module that performs side effects (like your current code is attempting to do), for example:
const func = require('./func.js');
class A {
constructor() {
}
}
A.prototype.func = func;
I'm newer to using Protractor for automation, so forgive me if this ends up being a dumb question. I have a helper.js module with a bunch of functions that I or other team members can use. One of the functions from helper.js needs to call to one of the existing functions in the module.
Is this possible? I have tried several different ways to do this and so far none have worked other than to break the helper functions into a separate js file that I need to call to.
Example:
helper.js:
module.exports = {
newbrowsertab: function(){
<code>
},
anotherfunction: function(){
<code>
<call to newbrowsertab();>
<code>
},
anotherfunction2: function(){
<code>
}
};
In the call to the newbrowsertab function, I've tried:
module.newbrowsertab();
this.newbrowsertab();
self.newbrowsertab();
You could use Prototypal inheritance then:
// helper.js functions
// create object
var Util = function() {};
// extend object
Util.prototype.enterPassword = function() {
// code
};
// extend object
Util.prototype.clickLogin = function() {
// code
};
// use `this` to call functions in same module
Util.prototype.fullLogin = function() { // extend object
this.enterPassword();
this.clickLogin();
};
module.exports = new Util();
Then in your test file:
var Util = require('./path/to/helper.js);
Util.fullLogin();
etc...
Expanding on the prototypal convention.
Any functions that are only helpers for other exported functions could be named with an underscore and declared to be executed later.
function _helperFunction(){
// do something
// return something
}
var exposedFunction = function() {
// do something
var x = _helperFunction();
// Do something else
}
module.exports = {
exposedFunction : exposedFunction
};
I am having trouble getting this code structure to survive obfuscation with the Google Closure Compiler. Here's some sample code:
var MyModule = (function()
{
function myModule()
{
// Constructor
}
function moduleFoo(url)
{
// Method
}
function moduleBar()
{
// Method
}
myModule.prototype = {
constructor: myModule,
foo: moduleFoo,
bar: moduleBar
};
return myModule;
})();
Elsewhere in my code I need be able to write things like the following:
var myMod = new MyModule();
myMod.foo();
myMod.bar();
However the compiler is renaming everything (as expected). How can I make the prototype that I have defined available elsewhere in my code after obfuscation? I have tried exporting as follows:
// In place of the prototype object above
myModule.prototype['constructor'] = myModule;
myModule.prototype['foo'] = moduleFoo;
myModule.prototype['bar'] = moduleBar;
window['myModule'] = myModule;
But things seem to break down either when the prototype methods are called or when their corresponding closures are executed.
Any help is appreciated.
This exact pattern does not work well with Closure-compiler using ADVANCED_OPTIMIZATIONS. Instead, you will need to slightly refactor your code:
/** #constructor */
function MyModule()
{
// Constructor
}
(function() {
function moduleFoo(url)
{
// Problem using "this" keyword. Will require #this annotation.
}
MyModule.prototype = {
foo: moduleFoo
};
MyModule.prototype.bar = function() {
// "this" keyword works fine.
};
})();
Or like:
/** #const */
var MyNamespace = {};
(function() {
/** #constructor */
MyNamespace.MyModule = function() {};
MyNamespace.MyModule.prototype = {
constructor: function() {},
foo: function(url) {},
bar: function() {}
};
})();
With either of the above methods your exports should work correctly.
Note: The second option will only work with a compiler built from the latest source as it involves a bug that was just fixed last week.
I'm running into an issue with the Google Closure Javascript compiler with advanced optimization. As the documentation suggests, to preserve exported Javascript I do something like this:
var myClass = function() {
this["myFunc"] = this.myFunc;
this["myFunc2"] = this.myFunc2;
};
window["myClass"] = myClass;
myClass.prototype = {
myFunc: function() { alert("myFunc"); },
myFunc2: function() { alert("myFunc2"); }
};
The issue is that sometimes, for whatever reason, myFunc and myFunc2 don't get shortened, and I see code like this in the final output:
x.myFunc=x.myFunc;x.myFunc2=x.myFunc2;
This is obviously less than ideal.
How can I prevent this from happening?
Further experimentation has shown that there are certain keywords, e.g. 'get' that don't get compiled.
var myClass = function() {
this["get"] = this.get;
this["myFunc2"] = this.myFunc2;
};
window["myClass"] = myClass;
myClass.prototype = {
get: function() { alert("myFunc"); },
myFunc2: function() { alert("myFunc2"); }
};
Compiles into
function a() {
this.get = this.get;
this.myFunc2 = this.a
}
window.myClass = a;
a.prototype = {get:function() {
alert("myFunc")
}, a:function() {
alert("myFunc2")
}};
I still don't know what's causing it though.
I cannot duplicate your problem. If I go to http://closure-compiler.appspot.com/home and compile the following:
// ==ClosureCompiler==
// #compilation_level ADVANCED_OPTIMIZATIONS
// #output_file_name default.js
// ==/ClosureCompiler==
var myClass = function() {
this["myFunc"] = this.myFunc;
this["myFunc2"] = this.myFunc2;
};
window["myClass"] = myClass;
myClass.prototype = {
myFunc: function() { alert("myFunc"); },
myFunc2: function() { alert("myFunc2"); }
};
Then I get the following result:
function a(){this.myFunc=this.a;this.myFunc2=this.b}window.myClass=a;a.prototype={a:function(){alert("myFunc")},b:function(){alert("myFunc2")}};
The properties are renamed, as expected.
As for your second example, it has to do with a Closure Compiler concept known as externs. An extern is a symbol that will be predefined in the environment in which the JavaScript will run, such as window or document in the case of Web programming. Because such names are fixed in the environment, the Compiler cannot mangle these names.
If you look at the DEFAULT_EXTERNS_NAMES list in CommandLineRunner.java, you will see a list of files from the externs folder. The contents of these files define the externs that the Compiler knows about (you can also add your own externs). Both webgl.js and w3c_indexeddb.js define types with a property named get (WebGLContextAttributes and IDBObjectStore, respectively). By default, the Compiler does not know the type of this in myClass, so as far as it knows, this could refer to an instance of either WebGLContextAttributes or IDBObjectStore, in which case it would not be safe to rename get.
By using a combination of type annotations (such as #constructor) and Compiler options such as ambiguateProperties and disambiguateProperties, the Compiler can determine that this will always refer to a new instance of myClass and rename all of its references to get consistently. You can read more about these Compiler optimizations in Chapter 14 of Closure: The Definitive Guide.
From the example here, it looks like you need to explicitly export prototype methods outside of the constructor:
var myClass = function() {};
myClass.prototype = {
myFunc: function() { alert("myFunc"); },
myFunc2: function() { alert("myFunc2"); }
};
window["myClass"] = myClass;
myClass.prototype["myFunc"] = myClass.prototype.myFunc;
myClass.prototype["myFunc2"] = myClass.prototype.myFunc2;
This seems to work as advertised, though it seems like an odd optimization to me (all the repeated "prototype" references add a lot of bytes):
function a(){}a.prototype={a:function(){alert("myFunc")},
b:function(){alert("myFunc2")}};
window.myClass=a;
a.prototype.myFunc=a.prototype.a;
a.prototype.myFunc2=a.prototype.b;
The reason is that, with ADVANCED_OPTIMIZATIONS, the GCC treats myObject["a"] differently from myObject.a. Try the following:
var myObject = {};
myObject.attr1 = 3;
myObject["attr2"] = 4;
window["myObject"] = myObject;
It will compile to this, with ADVANCED_OPTIMIZATIONS:
window.myObject={a:3,attr2:4};
I think you'll see what you need to do now.