I would like to use Clojurescript to write a component within a Javascript framework but I can't work out how to create the constructor and call global variables within the object.
The framework creates views (within their own .js file) by reading their state from a saved jason file and reifying them in javascript (the views are code like so):
(function() {
var Title = function(json) {
view.View.call(this, json); // view is defined in another js file - global namespace
this.title = json.title;
this.el.addClass("title");
}
view.inherit(view.View, Title);
view.Title = Title;
view.types.Title = Title;
Title.prototype.json = function() {
return $.extend(view.View.prototype.json.call(this), {
type: 'Title',
title: this.title
});
}
Title.prototype.reflow = function() {
this.h2.quickfit(opts);
}
})();
I have seen how you create a Javascript object using deftype macro and Object:
(deftype Foo [a b c]
Object
(bar x (+ a b c x)))
I'm new to both javascript and clojurescript. I see that the anonymous function wrapping everything provides a scope for the view but not sure how to (or if I need to) so something equivalent in clojurescript.
So my questions are:
how do I create the constructor function for Title in this model??
And do how should I handle the calls to the view variable, such as view.inherit etc?
Thanks
This is a bit general of an answer, but you seem to be wanting to share code between both ClojureScript and JavaScript, so here is a primer and a few select tidbits on Using JavaScript Libraries in Clojure:
Exporting
Protecting symbols you declare from renaming is easy; just define :export metadata on any ClojureScript var, and the ClojureScript compiler will ensure that it is not munged.
For example, a function can be declared like this:
(ns example)
(defn ^:export hello [name]
(js/alert (str "Hello," name "!")))
It is then available, via the same name, in an external JavaScript context:
<script>
example.hello("Eustace")
</script>
Externs
In order to go the other way, and reference a variable declared externally from within your code, you must provide the Google Closure compiler with an "extern file", a .js file defining javascript variables which will not be munged. This file is passed to the Google Closure compiler, and when compiling, it will not munge any names defined in it.
For example, I can try compiling the following ClojureScript (utilizing the Raphael.js library) in advanced mode without an extern file.
(defn test []
(let [raphael (js/Raphael. 10 50 320 200)]
(. raphael (circle 50 50 50))))
When I call the test function, it will throw a javascript error that new Raphael(10, 50, 320, 200)).K is not a function. K is the munged name of the circle function from Raphael, and obviously can't be found, since it isn't defined anywhere. We need to tell the compiler to preserve the name circle, not munge it.
I can do this by creating the following externs.js file:
var Raphael = {};
Raphael.circle = function() {};
And use it when compiling my ClojureScript:
(closure/build "src/cljs" {:optimizations :advanced
:externs ["externs.js"]
:output-to "helloworld.js"})
Related
In this case, How do I access the variable and method declared in a file from another file?
File one
jQuery(function(t) {
var myVar = 'myValue',
e = function(t) {
console.log('myLog');
}
});
File two
jQuery(function($){
// ????
});
You don't. It has nothing to do with files (JavaScript largely doesn't care about files unless they're ES2015+ modules), it has to do with the fact that both myVar and e are entirely private to the anonymous function you're passing into jQuery in the first code block. Even other code outside that function in the same file would be unable to access them.
You'd have to change the first file to make that information accessible outside that function. You could do that by making them globals (blech), or by having a single global you use for all of your things like this with an object with properties for these things (slightly less "blech" :-) ), or by using something like Webpack and true modules.
It really depends on how you setup your scripts. For instance:
<script src="fileOne.js"></script>
<script src="fileTwo.js"></script>
Then you will be able to do the following:
File One:
- Declare variable x
File Two:
- Access variable x
I recommend taking a look at this, it'll help with understanding variable scope (however this doesn't cover ES6's let): https://www.w3schools.com/js/js_scope.asp
I'm trying to use Closure Compiler and Closure Library.
When I use the library everything is ok, I'm including "base.js" in my simulation and it works with all my javascript files.
The problem is present when I "compilate" my application: In the output file I've got a reference to a closure library'sfunction "goog.inherits".
From what I've read, it's not necessary to include "base.js" in production. I'm working on a library, so I don't want to force users to have a reference to the Closure Library.
How can I do?
Here is my code:
NM.ObjectEvent = function( type )
{
goog.base(this);
}
goog.inherits( NM.ObjectEvent, NM.Event );
And the script look like that:
java -jar compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --js_output_file myLib.js `find ../src/ -name '*.js'`
What you have heard does not apply to SIMPLE_OPTIMIZATIONS. With ADVANCED_OPTIMIZATIONS everything unused in base.js is removed, with SIMPLE_OPTIMIZATIONS only function local optimizations are performed and unused methods are not removed.
Regardless of the mode, if you use goog.inherits it will remain in some form. Something needs to do the work that goog.inherits does to setup the prototype chain.
Like John said, if you have references to goog.base and goog.inherits, you're referencing the library. Fortunately, you can emulate those functions... Something like this should work...
NM.ObjectEvent = function( type )
{
NM.Event.call(this, type);
}
(function(){
var temp = function(){};
temp.prototype = NM.Event.prototype;
NM.ObjectEvent.prototype = new temp();
}();
If you're using goog.base elsewhere (for example, to call superclass methods), then you'll need to do more work, but the above should suffice if you're only using base and inherits where shown in your original post.
I've got some function that allows to merge namespace, very similar to import when the module contains lot's of function (I expose an API with dozens of combinators)
It generates lots of var f = target.f; for every item from the export
function getNamespace(name, exports){
var output='';
for(var item in exports){
output += 'var ' + item + ' = '+name+ '.'+item + ';';
}
return output;
}
and usage:
var paco = require('./paco.js');
eval(paco.getNamespace('paco', paco));
// instead of paco.between(paco.start(),paco.content(),paco.end())
between(start(), content(), end())
Question:
I there a way to 'hide' the eval into the some function ? I don't want neither to mutate global namespace nor to call vm.runInThisContext, just need to add some local variables into the calling context after call function similar to require.
I mean I need something like
import('./paco');
// this should work like this
// var paco = require('./paco.js');
// var between = paco.between;
but without mutation of global and without eval in the calling scope.
tl;dr: No.
In order to understand why this is impossible, it's important to understand what Node is doing behind the scenes.
Let's say we define a function in test.js:
function foo() {
var msg = 'Hello world';
console.log(msg);
}
In traditional browser JavaScript, simply putting that function declaration in a file and pulling the file in with a <script> tag would cause foo to be declared in the global scope.
Node does things differently when you require() a file.
First, it determines exactly which file should be loaded based on a somewhat complex set of rules.
Assuming that the file is JS text (not a compiled C++ addon), Node's module loader calls fs.readFileSync to get the contents of the file.
The source text is wrapped in an anonymous function. test.js will end up actually looking like this:
(function (exports, require, module, __filename, __dirname) {
function foo() {
var msg = 'Hello world';
console.log(msg);
}
});
This should look familiar to anyone who has ever wrapped their own code in an anonymous function expression to keep variables from leaking into global scope in a browser. It should also start making sense how "magic" variables in Node work.
The module loader evals1 the source text from step 3 and then invokes the resulting anonymous function, passing in a fresh exports object. (See Module#_compile.)
1 - Really vm.runInThisContext, which is like eval except it does not have access to the caller's scope
After the anonymous wrapper function returns, the value of module.exports is cached internally and then returned by require. (Subsequent calls to require() return the cached value.)
As we can see, Node implements "modules" by simply wrapping a file's source code in an anonymous function. Thus, it is impossible to "import" functions into a module because JavaScript does not provide direct access to the execution context of a function – that is, the collection of a function's local variables.
In other words, there is no way for us to loop over the local variables of a function, nor is there a way for us to create local variables with arbitrary names like we can with properties of an object.
For example, with objects we can do things like:
var obj = { key: 'value' };
for (var k in obj) ...
obj[propertyNameDeterminedAtRuntime] = someValue;
But there is no object representing the local variables of a function, which would be necessary for us to copy the properties of an object (like the exports of a module) into the local scope of a function.
What you've done is generate code inside the current scope using eval. The generated code declares local variables using the var keyword, which is then injected into the scope where eval was called from.
There is no way to move the eval call out of your module because doing so would cause the injected code to be inserted into a different scope. Remember that JavaScript has static scope, so you're only able to access the scopes lexically containing your function.
The other workaround is to use with, but you should avoid with.
with (require('./paco.js')) {
between(start(), content(), end())
}
with should not be used for two reasons:
It absolutely kills performance because V8 cannot perform name lookup optimizations.
It is deprecated, and is forbidden in strict mode.
To be honest, I'd recommend that rather than doing something tricky with eval, do your future maintainers a favor and just follow the standard practice of assigning a module's exports to a local variable.
If you're typing it that often, make it a single-character name (or use a better editor).
According to this answer Global variables for node.js standard modules? there is global object the same as in browser there is window. So you can add key to that object
function getNamespace(exports) {
for(var item in exports){
global[item] = exports[item];
}
}
and use it as:
paco.getNamespace(paco);
no need for eval at all.
No. It's not possible to modify the local scope from an external module. Reason being, when eval is called in the external module, its context will be the external module, not the scope requiring the module.
In addition, vm.runInThisContext does not have access to the local scope, so that wont help you either.
I have a question related to the way of using the RequireJs.
Our app should grow a lot in the near feature, so the major problem is to prepare a skeleton that would be followed by the developers involved in the project.
we tried this kind of wrapper on RequireJS(trying to enforce the OOP approach):
//each file will contains such a definition
//this file should be located under: appNamespace/Client/Widget.js
//attaching the class definition to the namespace
with ({public : appNamespace.Client})
{
//using a Class defined in the file: appNamespace/DomainModel/DataTable.js
var dt = using('appNamespace.DomainModel.DataTable');
//Class definition
public.Widget = function(iName)
{
//private variable
var dSort = new dt.SortableTable();
this.Draw = function(iRegion)
{
//public method implementation
}
}
}
Or, we could use the natural RequireJS model like structure:
<pre><code>
//this module definition should be located in the file: appNamespace/Client/Widget.js
define('appNamespace/DomainModel/DataTable', function(dt)
{
function Widget(iName)
{
//private variable
var dSort = new dt.SortableTable();
this.Draw = function(iRegion)
{
//public method implementation
}
}
return Widget;
})
I would prefer the first example because:
1. it will enforce developers to write scripts in a more OOP style
2. it looks like the C# or Java notation - so it will allow a faster switching between the back-end code and the client code
3. I really don't like the model structure because it allows to write any style of code. More of that, it's not enough to define your class, you should define the API that this file is exposing - so you can actually define an API that has no relation to what that file contains.
So - why would I use the second example - the natural RequireJS model style?
Is there anything that I miss?
Any suggestion is welcome
I have a some JavaScript with a complex structure. Because I'm new comer to JavaScript (only understanding some basic concepts) I don't know how to use it properly.
I have two files : Circle.js and Line.js. In Circle.js, I want to use a class object defined in Line.js:
In file Circle.js :
Helper.using('py.Figures', function (ns) {
ns.Circle = function (params) {
// some additional methods and code here
}
}
And in Line.js is :
Helper.using('py.Figures', function (ns) {
ns.Line2Point = function (params) {
// some addition methods and code here
};
}
In Figures.Circle, in ns.Circle I want to use Line2Point but I don't know how.
I think it should be :
line = new ns.Line2Point(params);
But It seem doesn't work.
According to Helper Class, ns will point to helper.using, in this case py.Figures. Does it mean, ns is the same object/reference in both the files?
I don't think this is doable in Javascript directly across files. If they are part of the same namespace you could share some 'global' objects to achieve this have the line2points and circles attach themselves to that global object:
Ex:
var myShapesNameSpace = {};
Circle.js:
(function(){
var circle = {};
circle.method1 = function(){...}
circle.method2 = function(){...}
myShapesNameSpace.Circles = circle;
})(window.myShapesNameSpace = window.myShapesNameSpace || {}); //check if namespace object exists. Else create a new blank one.
Line.js:
(function(){
var line= {};
line.method1 = function(){...}
line.method2 = function(){...}
myShapesNameSpace.Lines= line;
})(window.myShapesNameSpace = window.myShapesNameSpace || {});
Now you can check for the existence of myShapesNameSpace.Circles or .Lines and call the corresponding methods accordingly.
You can include files in javascript and reference objects across files unless they are exported in some global form either via window or your define global
Welcome to Javascript, the shit parts. Require.js was designed precisely for this because the creators of JS, well, I guess thought that everyone would write every program in one file.
RequireJS
It was designed for web use but can be used elsewhere too (locally, with Node, etc.)