I am facing a strange case of closure. In the following code snippet, defaultConfig is maintained as a closure variable even though it has no use left once test has been initialized. To insure that config contains only copies of defaultConfig's members, I have used slice to process its elements, but it still remains in memory, which can be verified by placing a breakpoint inside render. Also, it can't be found in closure if it has not been touched or used at all. I am unable to find any reason. I am using Chrome.
function test() {
"use strict";
var defaultConfig = ["as", "lx", "ms", "sw"];
return view;
function view(config) {
if (!config) {
config = [];
for (var attr in defaultConfig) {
config.push(defaultConfig[attr].slice(0,1));
}
}
this.render = function() {
console.log(config);
}
}
}
Related
This seems like a problem that requires some JS expertize that I'm apparently not in posses.
I'm writing a scripting module for an app. The scripting and the app are in Javascript.
It will be used by developers to provide scripting extensions to various modules that run on demand or when triggered by something.
I've got a request to alter the way it works in order to simplify the coding of the scripts.
And I'm kinda stuck since I don't know how to proxy local variables inside the script into an external object.
This is a sample of what works currently:
// this is part of the app developer API
function compileScript(userScript, argSignature) {
let _scriptFunc_ = null;
/* scripting API - there are available in scripts */
const print = function(message, window) {
const msg = document.createElement("span")
msg.innerText = message;
document.getElementById("output").append(msg)
document.getElementById("output").append(document.createElement("br"))
};
/* end Scripting API section */
try {
const scriptSource = `
(async () => {
try {
${userScript}
} catch (err) {
//_errEmit.fire(err)
}
})()
`; // wrap the execution in async so they can use await in their userScript
// argument signatures are defined by the module that "compiles" the script
// they call it with those and use them inside the userScript
eval("_scriptFunc_ = function(" + argSignature + ") {\n" + scriptSource + "\n}");
}
catch (err) {
//EvtScriptEmitEvalError.fire(err); // script "compilation" exception
return null;
}
return _scriptFunc_.bind(this);
}
// USAGE
// have some context with "variables" inside accessible by the script
// changes to this might be propagated elsewhere if it has property getters/setter
let scriptData = {
something: 10
};
// define the script
let userScript = `
for (let i = 0; i < count; i++) {
this.something++; // this changes scriptData
print(this.something)
}
`
// "compile" and run
const script = compileScript.call(scriptData, userScript, "count")
script(5) // output: 11,12,13,14,15
console.log(scriptData.something) // 15
<pre id="output">
</pre>
Note the usage of "this." inside the script to refer to scriptData members.
The request they have is to access the properties of the scriptData object inside the script by simply referring to its properties as if they were variables inside the script.
This is how they would want to write it (note there is no "this." before something):
let userScript = `
for (let i = 0; i < count; i++) {
something++; // this changes scriptData
print(something)
}
`
They are fine with possible name collisions between parameters and members of scriptData, it is developer work to set that up correctly.
My problem, tough, is that I don't have any idea how to modify "compileScript" in order to inject members of scriptData as plain variables inside the script is such a way that they proxy to the scriptData object.
It is easy to define a function in the scope of compileScript like "print", but I have no ideas on how to do this concept of "proxy variables".
"with" is not available in strict mode which the app runs in.
JS Proxy class does not seem useful.
Deconstructing scriptData into variables can be done but those variables are no longer going to the scriptData object, they are local.
Defining property getters/setters is available only for objects, not for the compileScript function...
I cannot modify the scriptData object, the user passes it as is. I can only tweak the generation of the script so that it behaves as required.
It should also work in a web worker (so no global scope / window), since a script could be triggered at the completion of a web worker.
Any ideas?
You're looking for the with statement. It's the only way to make variable assignments become interceptable as property assignments on your scriptData object (apart from going the full way to transpiling the script code with something like babel).
If writable variables are not necessary, you could also use this technique to inject values into the scope of a function. I would recommend to use it anyway, instead of eval.
/* scripting API - there are available in scripts */
const api = {
print(message, window) {
const msg = document.createElement("p");
msg.textContent = message;
document.getElementById("output").append(msg);
},
};
/* end Scripting API section */
// this is part of the app developer API
function compileScript(context, userScript, parameters) {
const scriptSource = `
const { ${Object.keys(api).join(', ')} } = api;
with (this) {
return async (${parameters.join(', ')}) => {
"use strict";
try {
${userScript}
} catch (err) {
//_errEmit.fire(err)
}
};
}
`;
try {
return new Function('api', scriptSource).call(context, api);
} catch (err) {
console.warn(err); // script "compilation" exception
return null;
}
}
let scriptData = {
something: 10
};
// define the script
let userScript = `
for (let i = 0; i < count; i++) {
something++; // this changes scriptData
print(something)
}
`;
// "compile" and run
const script = compileScript(scriptData, userScript, ["count"])
script(5) // output: 11,12,13,14,15
console.log(scriptData.something) // 15
<pre id="output">
</pre>
I followed this https://wiki.gnome.org/Projects/GnomeShell/Extensions/StepByStepTutorial for Overwriting a function.
For example, I want to override the function _setupKeyboard() on the Keyboard class, but my override isn't invoked. The specific portion I want to change is this, to remove the if guard:
if (Meta.is_wayland_compositor()) {
this._connectSignal(this._keyboardController, 'emoji-visible',
this._onEmojiKeyVisible.bind(this));
}
I copied the function from the source, removed the part I didn't want, then set the replacement function like this:
const Keyboard = imports.ui.keyboard;
Keyboard.Keyboard.prototype._setupKeyboard = myOverride;
Why isn't my override being invoked and how can I achieve this?
There are two common reasons an override won't be invoked. If the method is invoked before your override is applied, or if the function is a callback set with Function.prototype.bind() which creates a new closure.
In this case, the function _setupKeyboard() is called before your override is applied. When GNOME Shell starts up, it creates an instance of Keyboard.KeyboardManager here:
// main.js, line #204
keyboard = new Keyboard.KeyboardManager();
By the time the keyboard variable has been assigned to the instance, a default Keyboard.Keyboard class has been created and the function _setupKeyboard() has already been called in Keyboard._init(), which is much sooner than your extension is loaded.
Since there's no way to easily fix that, your best option is to just re-create the one part of the code you want to run:
const Meta = imports.gi.Meta;
const Main = imports.ui.main;
const Keyboard = imports.ui.keyboard.Keyboard;
const originalSetup = Keyboard.prototype._setupKeyboard;
const modifiedSetup = function () {
originalSetup.call(this);
if (!Meta.is_wayland_compositor()) {
this._connectSignal(this._keyboardController, 'emoji-visible',
this._onEmojiKeyVisible.bind(this));
}
this._relayout();
};
function init() {
}
// Your extension's enable function (might be a class method)
function enable() {
let kbd = Main.keyboard.keyboardActor;
if (kbd !== null) {
if (!Meta.is_wayland_compositor()) {
kbd.__mySignalId = kbd._connectSignal(kbd._keyboardController, 'emoji-visible',
kbd._onEmojiKeyVisible.bind(kbd));
}
}
Keyboard.prototype._setupKeyboard = modifiedSetup;
}
function disable() {
let kbd = Main.keyboard.keyboardActor;
if (kbd !== null && kbd.__mySignalId) {
kbd.disconnect(kbd.__mySignalId);
kbd.__mySignalId = 0;
}
Keyboard.prototype._setupKeyboard = originalSetup;
}
This is not very pretty, but that is often the price of patching private code. I can also not guarantee that the code will do what you want, because I suspect the emoji key is hidden on X11 for a reason.
I am trying to port some javascript to actionscript 3. I am new to javascript, and whereas much of js is familiar, I am having a devil of a time deciphering some js code. A global variable, "action", seems to be defined in a function prototype call, which is then apparently referenced later as a function parameter in the body of the script:
Variable "action" Definition:
function SteppedAction(proUpdater, unbrInterval, slInterval) {
}
SteppedAction.prototype.getResult = function
SteppedAction_getResult(recipient)
{
this.subactions.push({
action: function(action)
{
// function body
},
prop: 0
});
return this;
};
In the body of the script, this same "action" seems to be referenced again in other functions, even though it appears to be defined as a function parameter itself in other anonymous functions and as an argument in the generatePl() function:
function generate () {
activeAction = new SteppedAction(updateProgressUI)
.executeSubaction(function(action) { ui.progressPanel.show(); }, 0)
.executeSubaction(function(action) { generatePl(subdivs,
dist, count, rate, level, action); });
}
I am using Sublime Text to help decipher the javascript, and when I hover over any of the "action" named variables anywhere in the script, whether as function parameters or function call arguments, it references the original "action" in the definition given above. I simply do not understand this. How does a function parameter "know" it is referring to the original variable definition, unless function parameters in anonymous functions can somehow obtain global scope? I do not use anonymous functions normally.
The code for the SteppedAction.executeSubaction() is:
SteppedAction.prototype.executeSubaction = function
SteppedAction_executeSubaction(subaction, proportion, name) {
proportion = (typeof(proportion) === "number" && proportion >= 0) ?
proportion : 1;
this.subactions.push({ action: subaction, proportion: proportion,
name: name });
return this;
};
Any help would be greatly appreciated.
The short answer to your question is that the variable name of a function declaration is independent of its surrounding body. The variable name will "shadow" any previously declared variable. For example:
const action = "local variable";
const myFn = (action) => console.log("inside fn, action is", action);
myFn("other variable")
You'll notice that in this case, even though the myFn has a function argument of action it is completely independent of the action in the outer scope.
Similarly, it's important to note that this.subactions is storing a function as action, and not executing the action. Here's an example:
const subactions = [];
const myFn = (action) => console.log("calling myFn with", action);
console.log("about to push subactions");
subactions.push({action: myFn});
console.log("action pushed");
console.log("calling myFn");
subactions[0].action("other variable");
Notice that, very similarly to the above, the action parameter of myFn isn't set until it's called, and it has nothing to do with the outer scope. I hope this clears up your questions.
Addendum
I was putting together your code snippets and here's the ES2015 version of your code, if it helps. Hopefully the syntax is a bit more understandable.
class SteppedAction {
constructor(proUpdater, unbrInterval, slInterval) {
this.subactions = []; // missing but presumably exists
}
getResult(recipient) {
this.subactions.push({
action: (a) => {},
prop: 0, // should this be proportion to match executeSubaction?
});
return this;
}
executeSubaction(subaction, proportion, name) {
proportion = (typeof(proportion) === "number" && proportion >= 0) ?
proportion : 1;
this.subactions.push({
action: subaction,
proportion: proportion,
name: name
});
return this;
}
}
const generate = () => {
const activeAction = new SteppedAction();
// ui is not defined here.
activeAction.executeSubaction(() => ui.progressPanel.show(), 0);
// several of these variables are not defined in the code snippet
activeAction.executeSubaction((action) => generatePl(subdivs, dist, count, rate, level, action));
}
So, here's what I found out (at least how the Sublime Text text editor seems to work) through fiddling with the text editor: when one defines a function at the global level, (named or assigned to a variable or object), that function is evidently assigned a namespace that is referenced within the rest of the program, including function parameter definitions and arguments.
Thus,
myArray.push(action: function(x){});
var action = function(x){};
function action(x){}
will all create a reference to themselves for future calls using the "action" identifier (including within function parameter signatures), so that the parameter, "action," in
var myFunction(action) {}
will reference all three of the function declarations above when the cursor is hovered over it, giving line numbers where each are defined/declared. This only seems to happen with global function declarations. Regular global variables with a single value ARE NOT referenced within function parameter signatures like function declarations are.
As long as this is a quirk in Sublime Text itself and not some strange convention in javascript, where global function declarations create a namespace issue within subsequent function parameter signatures, I can understand it, and ignore it in the future. Thanks for the help!
I know there's a lot of questions on Stack about JS Scope... but I ran into a specific problem that I'm unable to wrap my head around. I have a Javascript module that looks something like this (albeit dramatically simplified):
module.exports = {
$company: $('#id_company'),
$companyCtrl: null,
$jobType: $('#id_job_type'),
$jobTypeCtrl: null,
init: function() {
var _this = this;
this.$companyCtrl = this.$company.selectize({
onChange: function(value) {
_this.companyChanged(value);
}
})[0].selectize;
},
companyChanged: function() {
// Company changed has been fired and does a few things
// before it calls this:
this.updateJobType();
},
updateJobType: function() {
var _this = this;
$.ajax({
url:'/ajax-url',
data: {
'id': this.companyID
}
})
.done(function(data) {
// If our job type selectize() instance hasn't been setup,
// then create it now
if (_this.$jobTypeCtrl === null) {
// ------------
// PROBLEM BLOCK
_this.$jobTypeCtrl = _this.$jobType.selectize({
onChange: function(value) {
if (_this.currentModel !== 'wire_add') {
_this.jobTypeChanged(value);
}
}
})[0].selectize;
// ------------
}
// Reload and re-enable input
_this.$jobTypeCtrl.reloadFromOriginalInput();
_this.$jobTypeCtrl.enable();
});
},
}
Now, here's what I don't understand, if I move that "PROBLEM BLOCK" outside of the Ajax call, and put it back up into init(), it works fine. However, as far as I can tell, in it's current location, the scope (_this = this) is the exact same as it would be up in the init function.
And to be more specific, the problem I'm experiencing is that the "onChange" handler never fires when the code is inside of the Ajax handler, but the plugin instance is still created and functions as it otherwise should. However, if I move it up to the init(), the onChange handler fires without any other changes to the code
Any help to get me to wrap my head around this would be greatly appreciated. Thank you!
I had a similar issue, where you start chasing your own tail using objects.
The power of using modules, is that they have their own context. So once compiled, the file knows what vars and funcs are residing inside; this negates the need to track this bouncing from function to function, which becomes a nightmare, once you involve async callbacks.
I recommend rewriting your module with vars at the top and functions, so it's easier to call any function without trying to pass the correct _this/self context from here, there and everywhere.
Here's an untested re-write:
module.exports = {
var $company = $('#id_company'),
$companyCtrl = null,
$jobType = $('#id_job_type'),
$jobTypeCtrl = null;
function init() {
$companyCtrl = $company.selectize({
onChange: function(value) {
companyChanged(value); // <== invoke any function and treat them as black-box code
}
})[0].selectize;
}
function companyChanged() {
// Company changed has been fired and does a few things
// before it calls this:
updateJobType();
}
function updateJobType() {
$.ajax({
url:'/ajax-url',
data: {
'id': companyID
}
})
.done(function(data) {
// If our job type selectize() instance hasn't been setup,
// then create it now
if ($jobTypeCtrl === null) {
// ------------
// PROBLEM BLOCK
$jobTypeCtrl = $jobType.selectize({
onChange: function(value) {
if (currentModel !== 'wire_add') {
jobTypeChanged(value);
}
}
})[0].selectize;
// ------------
}
// Reload and re-enable input
$jobTypeCtrl.reloadFromOriginalInput();
$jobTypeCtrl.enable();
});
}
}
Here is a simplified snippet from some code I wrote for managing tablet gestures on canvas elements
first a function that accepts an element and a dictionary of callbacks and register the events plus adding other features like 'hold' gestures:
function registerStageGestures(stage, callbacks, recieverArg) {
stage.inhold = false;
stage.timer = null;
var touchduration = 1000;
var reciever = recieverArg || window;
stage.onLongTouch = function(e) {
if (stage.timer) clearTimeout(stage.timer);
stage.inhold = true;
if (callbacks.touchholdstart) callbacks.touchholdstart.call(reciever, e);
};
stage.getContent().addEventListener('touchstart', function(e) {
e.preventDefault();
calcTouchEventData(e);
stage.timer = setTimeout(function() {
stage.onLongTouch(e);
}, touchduration);
if (callbacks.touchstart) callbacks.touchholdstart.call(reciever, e);
});
stage.getContent().addEventListener('touchmove', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdmove) callbacks.touchholdmove.call(reciever, e);
} else {
if (callbacks.touchmove) callbacks.touchmove.call(reciever, e);
}
});
stage.getContent().addEventListener('touchend', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdend) callbacks.touchholdend.call(reciever, e);
} else {
if (callbacks.touchend) callbacks.touchend.call(reciever, e);
}
stage.inhold = false;
});
}
later I call registerStageGestures on a few elements (represented by 'View' objects) in the same page. Something like:
function View() {
var self=this;
..
function InitView() {
...
registerStageGestures(kineticStage, {
touchstart: function(e) {
// do something
},
touchmove: function(e) {
// do something
},
touchendunction(e) {
// do something
},
touchholdstart: function(e) {
// do something
},
touchholdmove: function(e) {
// do something
},
touchholdend: function(e) {
// do something
},
}, self);
Everything works fine, however I'm left wondering about two things in the implementation of registerStageGestures:
First, is it necessary to make inhold, timer and onLongTouch members of the stage ? or will closures make everything works well if they are local vars in registerStageGestures ?
Second, is it necessary to call the callbacks with '.call(receiver,' syntax ? I'm doing this to make sure the callback code will run in the context of the View but I'm not sure if it's needed ?
any input is much appreciated
Thanks!
First, is it necessary to make inhold, timer and onLongTouch members
of the stage ? or will closures make everything works well if they are
local vars in registerStageGestures ?
As far as registerStageGestures() is concerned, var inhold, var timer and function onLongTouch(e) {...}. would suffice. The mechanism by which an inner function has automatic access to its outer function's members is known as "closure". You would only need to set stage.inhold, stage.timer and stage.onLongTouch if some other piece of code needs access to these settings as properties of stage.
Second, is it necessary to call the callbacks with '.call(receiver,'
syntax ? I'm doing this to make sure the callback code will run in the
context of the View but I'm not sure if it's needed ?
Possibly, depending on how those callbacks are written. .call() and .apply() are sometimes used when calling functions that use this internally. In both cases, the first parameter passed defines the object to be interpreted as this. Thus, javascript gives you the means of defining general purpose methods with no a priori assumption about the object to which those methods will apply when called. Similarly, you can call a method of an object in such a way that it acts on another object.
EDIT:
For completeness, please note that even in the absence of this in a function, .apply() can be very useful as it allows multiple parameters to be specified as elements of a single array, eg the ubiquitous jQuery.when.apply(null, arrayOfPromises)...
There are some simple answers, here.
First, closure:
Closure basically says that whatever is defined inside of a function, has access to the rest of that function's contents.
And all of those contents are guaranteed to stay alive (out of the trash), until there are no more objects left, which ere created inside.
A simple test:
var testClosure = function () {
var name = "Bob",
recallName = function () { return name; };
return { getName : recallName };
};
var test = testClosure();
console.log(test.getName()); // Bob
So anything that was created inside can be accessed by any function which was also created inside (or created inside of a function created in a function[, ...], inside).
var closure_2x = function () {
var name = "Bob",
innerScope = function () {
console.log(name);
return function () {
console.log("Still " + name);
}
};
return innerScope;
};
var inner_func = closure_2x();
var even_deeper = inner_func(); // "Bob"
even_deeper(); // "Still Bob"
This applies not only to variables/objects/functions created inside, but also to function arguments passed inside.
The arguments have no access to the inner-workings(unless passed to methods/callbacks), but the inner-workings will remember the arguments.
So as long as your functions are being created in the same scope as your values (or a child-scope), there's access.
.call is trickier.
You know what it does (replaces this inside of the function with the object you pass it)...
...but why and when, in this case are harder.
var Person = function (name, age) {
this.age = age;
this.getAge = function () {
return this.age;
};
};
var bob = new Person("Bob", 32);
This looks pretty normal.
Honestly, this could look a lot like Java or C# with a couple of tweaks.
bob.getAge(); // 32
Works like Java or C#, too.
doSomething.then(bob.getAge);
? Buh ?
We've now passed Bob's method into a function, as a function, all by itself.
var doug = { age : 28 };
doug.getAge = bob.getAge;
Now we've given doug a reference to directly use bobs methid -- not a copy, but a pointer to the actual method.
doug.getAge(); // 28
Well, that's odd.
What about what came out of passing it in as a callback?
var test = bob.getAge;
test(); // undefined
The reason for this, is, as you said, about context...
But the specific reason is because this inside of a function in JS isn't pre-compiled, or stored...
this is worked out on the fly, every time the function is called.
If you call
obj.method();
this === obj;
If you call
a.b.c.d();
this === a.b.c;
If you call
var test = bob.getAge;
test();
...?
this is equal to window.
In "strict mode" this doesn't happen (you get errors really quickly).
test.call(bob); //32
Balance restored!
Mostly...
There are still a few catches.
var outerScope = function () {
console.log(this.age);
var inner = function () {
console.log("Still " + this.age);
};
inner();
};
outerScope.call(bob);
// "32"
// "Still undefined"
This makes sense, when you think about it...
We know that if a function figures out this at the moment it's called -- scope has nothing to do with it...
...and we didn't add inner to an object...
this.inner = inner;
this.inner();
would have worked just fine (but now you just messed with an external object)...
So inner saw this as window.
The solution would either be to use .call, or .apply, or to use function-scoping and/or closure
var person = this,
inner = function () { console.log(person.age); };
The rabbit hole goes deeper, but my phone is dying...